Revision number: 13
Status: release
Revision History
Date |
Revision |
Description |
30.05.2001 |
1 |
Draft version compiled from multiple sources |
30.05.2001 |
13 |
Added some corrections |
31.05.2001 |
20 |
Added some corrections |
31.05.2001 |
21 |
Merged modifications, release version |
31.05.2001 |
22 |
Minor modifications |
07.06.2001 |
27 |
Maximum line length increased |
07.09.2002 |
50 |
Updated for Microsoft.NET |
05.12.2002 |
80 |
Changes based on vast experience with coding in VS.NET. |
10.04.2006 |
|
Added SQL Code and RDBMS objects naming requirements |
Table of contents
1. Introduction
1.1. Purpose
1.2. Scope
2. General principles
3. C# Code
3.1. Formatting
3.1.1. Spacing
3.1.2. Maximum line length
3.1.3. Indentation
3.1.4. Methods
3.1.5. Cycles
3.1.6. If statement
3.1.7. Switch statement
3.2. Commenting
3.2.1. Beginning of file
3.2.2. Classes
3.2.3. Methods
3.2.4. Code
3.2.5. Temporary comments
3.3. Naming
3.3.1. Variables
3.3.2. Constant names
3.3.3. Method names
3.3.4. File names
3.4. Contract based programming
3.5. Notes in code
3.6. Use regions
3.7. QA
3.8. Example
4. SQL Code and RDBMS Objects
4.1. Naming
4.1.1. Tables
4.1.2. Views
4.1.3. Functions
4.1.4. Stored procedures
4.1.5. Indexes
5. OLAP Objects
5.1. Cubes
5.2. Dimensions
6. Security Considerations
7. VS.NET 2005 settings
8. References
1. Introduction
1.1. Purpose
The purpose of this document is to describe and set single programming style. This document is addition to guidelines described in Microsoft's Design Guidelines for Class Library Developers.
1.2. Scope
This document is applicable to any code written.
2. General principles
Good visual layout shows logical structure of the program. All following guidelines follow this rule. Developers should follow this rule in all cases not covered below.
Also developers should follow Microsoft Visual Studio.NET C# editor predefined coding style recommendations.
3. C# Code
3.1. Formatting
3.1.1. Spacing
Use spaces to increase readability:
rawCount = getRawCount( dataSet ) + 1;
Don’t insert space between empty parentheses:
connection = getConnection();
Put space line between blocks of code. Code blocks longer then ~15 lines are not allowed.
//create handler parameters string
string parameters = "";
for( int i = 0; i < handlerArguments.Length(); i++ )
{
if ( parameters != "" )
...
}
int eventResult = null; //result of the event
// call all system handlers registered for this event (registered for event name,
// they don't care about object)
if ( this.eventList.Exists( eventName ) )
{
...
Array indexes: a[ 1 ]
Empty lines between functions: 2 lines and comment header
3.1.2. Maximum line length
Wrap lines if their length exceeds ~100 characters.
Incorrect:
if (value != null && value != string.Empty)
{
checkCorrection = (Regex.Match(value, "\\s*(\\s*\\w+\\s*(as\\s*\\w+){0,1}\\s*,)*\\s*\\w+\\s*(as\\s*\\w").Captures.Count == 1) || (Regex.Match(value,"\\s*\\*\\s*").Captures.Count == 1);
Debug.Assert(checkCorrection, " Trying to set incorrect SelectFields value in SQLQueryBuilder!");
}
Correct:
if ( value != null && value != string.Empty )
{
string regExp1 = "\\s*(\\s*\\w+\\s*(as\\s*\\w+){0,1}\\s*,)*\\s*\\w+\\s*(as\\s*\\w";
string regExp2 = "\\s*\\*\\s*";
bool condition1 = (Regex.Match(value, regExp).Captures.Count == 1);
bool condition2 = (Regex.Match(value, regExp2).Captures.Count == 1);
checkCorrection = condition1 || condition2;
Debug.Assert(
checkCorrection,
"Trying to set incorrect SelectFields value in SQLQueryBuilder." );
}
3.1.3. Indentation
To indent block of text use tab symbols. Do not use spaces. This is default in Visual Studio.NET.
3.1.4. Methods
Use brackets style as follows. See comments section also.
static int GetRawCount( int RecordsetId )
{
int i = 0;
...
return Raw;
}
3.1.5. Cycles
for ( int i = 0; i < this.GetRawCount( RecordsetId ); i++ )
{
UpdateRaw( i );
...
}
3.1.6. If statement
Always use brackets in if statement.
if ( type == null )
{
break;
}
else
{
continue;
}
3.1.7. Switch statement
switch ( iRawType )
{
case RAW_TYPE_1:
UpdateRaw( iRawId );
...
break;
case RAW_TYPE_2:
...
return;
default:
throw new ApplicationException( "incorrect raw type" );
} //switch
3.2. Commenting
3.2.1. Beginning of file
//-----------------------------------------------------------------------------
//
// Copyright 2002 Vovik. All Rights Reserved.
//
// $Workfile: SqlQueryBuilder.cs $
// $Revision: 3 $
// $Author: Pyurasov $
// Last checked-in at $Date: 6.09.02 15:18 $
// $Modtime: 6.09.02 15:17 $
//
// Description: this file implements SqlQueryBuilder class
//
//-----------------------------------------------------------------------------
// $Archive: /ecommerce/Libraries.NET/AspClassLibrary/SqlQueryBuilder.cs $
#region Changelog
/* $Log: /ecommerce/Libraries.NET/AspClassLibrary/SqlQueryBuilder.cs $
*
*
* 3 19.04.02 18:53 Pyurasov
*
* 2 19.04.02 18:24 Pyurasov
*
* 1 18.04.02 22:11 Pyurasov
*/
#endregion
Copy preceding text to beginning of file and replace description field contents — other information will be filled-in automatically by Visual Source Safe (it does so for fields $FIELD xxx $).
3.2.2. Classes
/// <summary>
/// <para>
/// Builds parts of query for use on pages that display information from
/// base table and display filters to search through this information.</para>
/// <para>
/// Set BaseTableName, BaseTableAlias (optionaly), register
/// filters via RegisterFilter, register sorters via RegisterSorter,
/// register scrollers via RegisterScroller then call GetFromPartString,
/// GetWherePartString and GetOrderByPartString. You can get from this
/// component SQL query text or data table filled according query and scrolling
/// information. </para>
/// See also <see cref="MyClass.Main"/>
/// </summary>
[Designer("org.AspClassLibrary.Design.SqlQueryBuilderDesigner")]
public class SqlQueryBuilder: Component
{
...
Pay attention that comments start with "///" and are XML, spacing rules are XHTML ones. Use following tags:
<c> <para> <see> <code> <param> <seealso> <example> <paramref> <summary> <exception> <permission> <value> <include> <remarks> <list> <returns>.
Check MSDN for meaning of the tags.
Use NDOC tool to generate CHM documentation.
3.2.3. Methods
Always comment and layout methods as following.
/// <summary>
/// StateChange event handler for connection.
/// See <see cref="System.EventArgs.StateChangeEventHandler"/>
/// </summary>
/// <param name="sender">object that sends an event</param>
/// <param name="args">event agruments object</param>
protected void OnStateChange( object sender, StateChangeEventArgs args )
{
Trace.Write( string.Format(
"Default connection state has changed from '{1}' to '{2}', DB: {0}",
((IDbConnection)sender).Database,
args.OriginalState,
args.CurrentState) );
}
Comments should explain:
-
what the method does (not how)
-
each parameter
-
explanation of value returned by function
-
list of each affected external variable, control, file, and so on and the affect it has
You may omit some comment fields in rare cases if their content is undoubtedly obvious.
3.2.4. Code
Try not to use end line comments. Exception is declaring variables, end of big bracketed block.
if ( id > 0 )
{
int rawId = 0; // current raw number
// create and init new raw
int raw = new Raw();
int paramId = getParam( "id" );
raw.id = ( isEmpty( iParamId ) ) ? null : iParamId;
...
} // if ( id > 0 )
3.2.5. Temporary comments
To temporary comment out code use VS.NET commenting feature (Ctrl+K, Ctrl+C and Ctrl+K, Ctrl+U). This puts comments to beginning of line.
// if ( IsDeleted )
// return;
3.3. Naming
Use descriptive names. Good name has between 10 to 20 characters. Use abbreviations if necessary. Always use abbreviated Max, Min, Lang, Db. When using abbreviations put only first letter uppercase like: HttpConnector, DbType.
3.3.1. Variables
3.3.1.1. Prefixes
Use '_' prefix when naming variables — private member of class. Start with lower case letter: _dbType, _baseTableName.
3.3.1.2. Local variable names
Use uppercase letters to separate words except first one.
Examples: eventManager, systemLangId.
3.3.1.3. Special cases
You may use variables "i", "j", "k", "l", "m", "n" of type integer as loop variables or in other cases when their scope is no longer then one screen of code.
Use variable "s" of type string when its scope is no longer then 10-15 lines of code.
Avoid using "temp" variable.
Use variable like "conn" if it represents one and only object of that type (in this case IDbConnection) in very narrow scope (no more that one screen of code).
3.3.2. Constant names
Name constants with all uppercase letters, divide words with underscores.
Examples: MAX_RETRIES, WRONG_PARAM_ERROR.
3.3.3. Method names
Name methods starting with verbs like Get, Set, Add, Update, Remove, etc. If needed add adjective like New, Last, etc. Add noun like Record, Connection, ErrorMessage etc at the end.
Examples: GetNewRecord, UpdateCustomerInformation, IsEmpty.
3.3.3.1. Special cases
3.3.3.1.1. Functions converting types
Use "To" between type names.
Example: IntToString, UnicodeToChar.
3.3.4. File names
Generally there should be one file per class. File should be named same as class. File may additionally contain some very relative small classes.
3.4. Contract based programming
To improve reliability of code, shorten debug time and increase supportability methods should check
-
incoming parameters
-
state of the object
-
outgoing return values
Method cannot make any assumptions about who, how and when calls it.
Use Debug.Assert to check conditions that may be checked only at debug time. If condition is not obvious add explanation as second parameter.
Use #ifdef DEBUG / #endif for complex conditions.
Some conditions should be checked at run-time also.
public string UnregisterFilter( IBaseFilter filter )
{
// precondition
Debug.Assert( DesignMode, "This method is for design mode only." );
Debug.Assert( filter != null );
#ifdef DEBUG
foreach( SubFilter subFilter in filter.SubFilters )
{
if ( subFilter.Param == null )
{
throw new ApplicationException( "Subfilter Param cannot be null." );
}
}
#endif
string retVal = _registeredFilters.Remove( filter );
// invalidate cache
_cacheIsReady = false;
// postcondition
Debug.Assert( retVal != null, "Return value cannot be null." );
return retVal;
}
3.5. Notes in code
When there is note to be left in comments put a special word in the beginning of the line. This can be easily tracked via Visual Studio Task List.
// TODO: check for additional conditions
// HACK: this code works but further investigation is required
3.6. Use regions
To divide code into collapsible regions use #region.
3.7. QA
Code should be reviewed by peers periodically. Following tools may aid with checking the quality:
-
Visual Studio's feature to put warning if comments are missing
-
Microsoft's FxCop tool
-
NDoc — CHM generation tool
Note: Developers are required to fix their coding style in extra time (AKA work for free).
3.8. Example
//-----------------------------------------------------------------------------
//
// Copyright 2002 Vovik Ltd. All Rights Reserved.
//
// $Workfile: SqlQueryBuilder.cs $
// $Revision: 20 $ VSS version number
// Last $Author: Pyurasov $ checked-in at $Date: 6.09.02 15:18 $
// $Modtime: 6.09.02 15:17 $
//
// Description: this file implements SqlQueryBuilder class
//
//-----------------------------------------------------------------------------
// $Archive: /ecommerce/Libraries.NET/AspClassLibrary/SqlQueryBuilder.cs $
#region Changelog
/* $Log: /ecommerce/Libraries.NET/AspClassLibrary/SqlQueryBuilder.cs $
*
* 1 19.04.02 18:53 Pyurasov
*
* 1 19.04.02 18:24 Pyurasov
*
* 4 18.04.02 22:11 Pyurasov
*/
#endregion
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.ComponentModel;
using System.Diagnostics;
using org.ClassLibrary;
using org.AspClassLibrary.Filters;
using org.AspClassLibrary.Sorters;
using org.AspClassLibrary.Scrollers;
// TODO: add support for M:M filters
namespace org.AspClassLibrary
{
/// <summary>
/// Builds parts of query for use on pages that display information from
/// base table and display filters to search through this information.
/// ...
/// </summary>
[Designer( "org.AspClassLibrary.Design.SqlQueryBuilderDesigner" )]
[AnotherSampleAttribute()]
public class SqlQueryBuilder: Component
{
#region Private fields
/// <summary>
/// value of BaseTableName property
/// </summary>
private string _baseTableName;
/// <summary>
/// value of BaseTableAlias property
/// </summary>
string _baseTableAlias;
#endregion
#region Properties
/// <summary>
/// Generates SQL query for design time and for filling in
/// dataset in runtime.
/// </summary>
[Browsable( false )]
public string Sql
{
get
{
string fieldSet = (_selectFields || _selectFields);
string sql = "SELECT " + fieldSet + " FROM " +
GetFromPartString();
string wherePart = GetWherePartString();
if ( wherePart != null && wherePart.Length > 0 )
sql += " WHERE " + wherePart;
sql += GetOrderByPartString();
return sql;
}
}
#endregion
#region Methods
/// <summary>
/// Unregisters filter. Used in design mode when filters can be
/// added and removed dynamicaly. Supports null values.
/// </summary>
/// <param name="filter">Filter to unregister</param>
public void UnregisterFilter( IBaseFilter filter )
{
Debug.Assert( DesignMode, "This method is for design mode only." );
_registeredFilters.Remove( filter );
// invalidate cache
_cacheIsReady = false;
}
#endregion
}
}
4. SQL Code and RDBMS Objects
4.1. Naming
Follow the same rules for RDBMS objects as for C# code objects (see 3.3). For other cases not listed here refer to AdventureWorks sample database shipped with Microsoft SQL Server 2005.
4.1.1. Tables
Name tables in Pascal-style in singular form. E.g.
ProductModel
ProductDescription
ContactCreditCard
For association tables (that implement M-M relationship) construct names by concatenating names of the tables that are related using this association table.
ProductModelProductDescription
Name columns in Pascal-style. E.g.
Name
ModifiedDate
Name table’s primary key column as TableNameID, e.g.
ProductModelID
ProductDescriptionID
Name foreign key columns just as primary key columns in referenced tables. E.g.
ProductModelID
ProductDescriptionID
Name bit columns with Flag postfix
MakeFlag
FinishedGoodsFlag
4.1.2. Views
Name views in Pascal-style and add ‘v’ prefix to indicate it is a view. E.g.
vIndividualCustomer
vSalesPersonSalesByFiscalYears
4.1.3. Functions
Name functions in Pascal-style and add ‘ufn’ prefix to indicate it is a user function. Also use Get verb in the beginning. E.g.
ufnGetAccountingEndDate
ufnGetProductListPrice
4.1.4. Stored procedures
Name stored procedures in Pascal-style and add ‘ufp’ prefix to indicate it is a user stored procedure. Also use Get verb in the beginning of name if stored procedure produces result set. E.g.
uspGetBillOfMaterials
uspLogError
Name stored procedure parameters and local variables in Pascal-style, like table columns, e.g.
@ManagerID
@EmployeeID
4.1.5. Indexes
Name indexes according to the following pattern IX_<TableName>_<IndexedColumnNames>, e.g.
IX_SalesOrderHeader_SalesPersonID
IX_SalesOrderHeader_CustomerID
IX_SalesOrderHeader_CustomerIDSalesPersonID
5. OLAP Objects
5.1. Cubes
Give cubes meaningful names, use spaces to separate words in cube name, e.g.
Sales
Purchases
Give measures meaningful names, use spaces to separate words in cube name, e.g.
Internet Sales
Internet Orders
5.2. Dimensions
Name dimensions in singular form, use spaces to separate words in dimension name, e.g.
Product
Sales Territory
Organization
Name dimension levels in singular form, use spaces to separate words in dimension name, e.g.
Category
Subcategory
Model Name
6. Security Considerations
This document has no relation to any security considerations.
7. VS.NET 2005 settings
Pleased use default formatting settings except turn ON the following::
-
Insert space within argument list parentheses
-
Insert space within argument list parentheses
-
Insert space within parentheses of expressions
-
Insert space inside parentheses of control flow statements
-
Insert spaces within square brackets
-
Insert space after colon for base or interface in type declaration
8. References
-
McConnell, Steve. Code Complete. Redmond, WA: Microsoft Press, 1993.
-
MSDN: .NET framework / Reference / Design Guidelines for Class Library Developers
No comments:
Post a Comment