Coding Style

Indentation

Use tabs, not spaces, to indent your code.  Indent one tab with each deeper level of code block or construct.  In general, when you open a code block with a bracket “{“, the next line will likely be further indented by one tab.

Example:
Class TestClass
{
       // New indentation (tab) level

public void SomeMethod()
{
               // New indentation (tab) level
      
               if (...)
               {
                      // New indentation (tab) level
               }
}
}

Braces

Use the vertical pair-matching style of bracing.  Example:

if (currentIndex >= INDEX_MAX)
{
       return _partNames[INDEX_MAX];
}
else
{
       return _partNames[currentIndex];
}

In addition, while braces are syntactically optional for constructs such as if and for having only one line to execute, include the braces for readability and maintainability (as in the above example.)

Empty code regions can be represented with “{}” on the same line.  For example:

public class MyClass
{
       private MyClass() {}

       ...
}

Also, property get and set accessors, when they contain a single line of code, can optionally be written on the same line for brevity.  For example:

public string AgeInYears
{
get {return _ageInYears;}
set {_ageInYears = value;}
}

Data Type Declarations

When specifying data types, such as for variable declarations or method return values, use the system keyword aliases (e.g. int, float, string) instead of the base types (e.g. System.Int32, System.Single, System.String).

Example:
int i;            // Not Int32 or System.Int32
string userName;  // Not String or System.String
char c = ‘x’;     // Not Char or System.Char

Placement of Variable Declarations

Declare variables as closely as possible to first use and within the deepest possible scope.  A benefit of this is that as code is modified over time, variable declarations and instantiations are less likely to be orphaned at the top of pages or methods.  Tip - For help finding a declaration of a variable in Visual Studio.Net, simply right-click on the variable name and choose “Go To Definition”.

Example:
public string GetPartNameLameExample(int partID)
{
       // NOT HERE
       // string partName;

       if (partID != MAGIC_PART_NUMBER)
       {
               // HERE – scope to deepest possible block
               string partName = GetPartName(partID);
               if (partName.Length > 20)
               {
                      return partName.Substring(0,17) + “...”;
               }
               return partName;
       }
       else
       {
               return MAGIC_PART_NAME;
       }
}

This also applies to “for” loops and similar constructs:

for (int i=0; i <= someValue; i++)
{
       // variable “i” is scoped to this block only
}





Constants

Do not use “magic numbers” in your code.  Replace with constants and your code will be more readable and maintainable.

Example:
int timeout = hours * 3600; // NO, NO, NO!

const int SECONDS_PER_HOUR = 60 * 60;
int timeout = hours * SECONDS_PER_HOUR;

Do not create public constants in classes, instead use static readonly fields.  Constants can be compiled into referencing assemblies, so a change to the original would require recompilation of the consuming assembly to effect a change.

Namespaces and the using Keyword

Avoid using fully-qualified type names in your code.  Instead, add a using keyword with the appropriate namespace at the top of your code and use the shortened version.  

Do not use using keywords inside a namespace declaration.

Order using keywords alphabetically, but group them by listing standard Microsoft references first, then custom or third party references after:

Example
using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using ThirdParty.Tool;
using Global.Database;
using Global.Translations;
using Monster.User;

namespace Monster.Example
{
   ...
}

Avoid having extraneous using keywords of namespaces that you aren’t actively calling in your code (for example, Visual Studio includes a number of default usings to each new code file, some can be removed if you aren’t using that specific functionality, such as System.Web.SessionState.)  While they won’t cause any problems, a shorter list is simply easier to read and maintain.

Enumerations and the Flags Attribute

If an enumeration represents combinable settings (e.g. bitmasks), mark the enum with the [Flags] attribute above the definition.  Also, per the section on naming, use a plural name for the enumeration itself.  Note that [Flags] does not assign values, you still should set the values manually (as in the example below.)

Examples
[Flags]
public enum Bindings
{
IgnoreCase = 0x01,
NonPublic = 0x02,
       Static = 0x04,
All = IgnoreCase | NonPublic | Static
}

Switch Statements

When using a switch statement that would normally not require a default case, ensure that you add one which throws an exception and/or asserts false.  This is especially true when your switch argument is based on an enumeration – another developer could extend that enumeration and your switch statement would not have had that specific value as a case.

switch (someValue)
{
case Things.Alpha:
       ...
               break;

       case Things.Beta:
               ...
               break;

default:
       throw new Exception(String.Format(“Unknown \”Things\” enumeration value: \”{0}\”.”, someValue));
}

Conditional Methods

Avoid compiling out methods using #if/#endif constructs.  Instead, use the Conditional attribute under System.Diagnostics.  Note that methods decorated in this way must return void because they, and the code that calls them, are compiled out if the conditional attribute is false.

Attributes

Place all assembly-level attributes (e.g. [assembly: FileIOPermission(…)] ) in AssemblyInfo.cs.

Parameter Declaration (Reference vs. Value)

By default, parameters to methods are passed by value.  For value types (int, byte, long, etc.), this is straightforward, a copy of the value is sent, if changed in the method, there is no effect to the original variable.  Reference types (e.g. a variable pointing to a DataSet instance,) have a copy of their reference (or pointer) passed by value, not the object.  This means the method has access to and can change the referenced object via the shared pointer.

Use parameter qualifiers to identify and limit the use of a parameter whenever possible.  If a parameter may change in the call, and could be returned modified, use ref.  However, instead of simply declaring a parameter as ref when it is not read, only written to, use out instead.

How does ref affect a reference type?  The only difference between “MyMethod(ref Customer c)” and “MyMethod(Customer c)” is that if the method assigned a different instance of a Customer class to c, the ref version would also assign that new instance in the caller’s reference, the non-ref version would be assigned only locally to the method and (typically) destroyed when the reference goes out of scope at the end of the method call.

Keep in mind that while a string is a reference type, it is immutable, so if you pass a string reference by value and assign a new string value, you are actually assigning a new string instance and therefore the change will not affect the caller.  To have the new string reflected in the caller, use the ref keyword.
 
[For a proper description, far better than this, see “Value and Reference Parameters” in Inside C#, 2nd Edition by Archer and Whitechapel.]

Critical Regions and Multithreaded Operations

In multithreaded applications, a critical region is a block of code allowing only one thread to execute at a time.  To create a critical region, use the lock statement and target the current object instance with the this keyword:

lock(this)
{
    // critical code
}

The above will lock the region from all threads using the same instance of the class.  There are times when you want to ensure a region is synchronized across all instances of the class.  To do this, you can lock on a private static reference-type variable.  If you do not already have one, create a “dummy” one specifically for locking use:

private static object _lockVariable = new object();
...
lock(_lockVariable)
{
    // critical code
}

Warning: Never write code that locks a Type object:

lock(typeof(TargetClassName))  // No!  Evil!!!
{
    // critical code
}

This is a commonly documented approach to locking a region for all instances of a class, but unfortunately suffers from a number of issues as described in: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaskdr/html/askgui06032003.asp  Use the private static variable approach described above instead.


Checked and Unchecked Arithmetic Operations

To improve performance, arithmetic and casting operations in C# are, by default, performed in unchecked mode, where overflows result in truncation of the final result.  Truncation is done by dropping any high-order bits outside the limit of the target type.  This generally results in values that are not useful.  For example:

short s = 257;
byte b = (byte) s;

The value of b is 1 after the cast.  Why?  The short value 257 is bit 9 (=256) and bit 1 (0000 0001 0000 0001.)  Since bit 9 is outside the scope of short (which is only 8 bits,) it is dropped, leaving just bit 1 set (0000 0001.)

If you have equations or casts which may truncate, wrap the code in a checked region and handle any resulting OverflowException.  If you are certain that equations cannot result in overflow or underflow, use the default unchecked context.  Avoid the “/checked” project compilation option, but if you must use it, note the fact in your code documentation.

Example:
int x = Convert.ToInt32(Console.ReadLine());
int y = Convert.ToInt32(Console.ReadLine());

int uncheckedTotal = x + y;  // This would never throw an error, but might truncate
short a = (short) x;         // This would never throw an error, but might truncate

try
{
    int total = checked(x + y);  // This might fail, throwing OverflowException
    short b = checked((short) x);         // This might fail, throwing OverflowException
}
catch(OverflowException exp)
{
    // Handle case
}


Case Sensitivity

While C# is case-sensitive, classes and web services may be exposed to environments that are not case-sensitive (e.g. VB.Net.)  Therefore, do not name entities of the same level (e.g. namespace, method, property) that differ only in case.  For example, having two properties, “userID” and “UserID” will work in C#, but will not be distinguishable in VB.Net and will result in a non CLR-compliant assembly.

Namespaces

Namespace Naming

The namespace of a custom object should begin with the name of the major group under which the work is being done.  There are three general rules for root naming:

1.      Classes that will be used only internally to a project or application should be named with the root of “<Company Segment>.<ProjectName or Abbreviation>”.  For example, “MonsterMoving.Forums” or “Monster.Timesheets”.

2.      Classes that will be used across multiple applications under the same company segment should be named using the root “<Company Segment>.”.  For example, “Monster.” or “MonsterMoving.”.

3.      Classes that will be used across company segments should be named using “Global.”. 

Any classes that need to follow rules 2 or 3 must be approved by the .NET Architecture group (contact Chris Bowen or Tim Weaver.)

The root should be followed by one or more grouping names to indicate specific functionality and/or membership with other classes.  For example, “Monster.Resume” or “Monster.Resume.Conversion”

Namespace Structure


Each “dot level” of a namespace should be organized into a separate directory.  For example, “Monster.Resume” would have two directories, “\Monster” and “\Monster\Resume”.  In addition, each level of a hierarchy should generally be built into a separate assembly for ease of deployment as changes are made (e.g. “Monster.Resume.dll” and “Monster.Resume.Conversion.dll”.)  There may be cases where it is not practical to divide namespaces into separate assemblies, typically where circular references would result, but this should be the exception, not the norm.

Each dot level of a namespace may be dependent upon the classes in the shallower levels, but not the reverse.  For example, “Monster.Resume.Conversion” may use classes found in “Monster.Resume”, but “Monster.Resume” classes should never depend upon those contained in “Monster.Resume.Conversion”.

Namespace Aliasing

If referenced namespaces have an overlap in, say, a class name, use an alias to distinguish them.  The alias is typically the first letter of each word (not just each dot level) in the namespace.  Prefer to alias a custom (i.e. Monster) namespace over a standard Framework one to aid readability.  In other words, if “Monster.Useless.BadExamples” had a class named “DataColumn”, it would be in conflict with System.Data.DataColumn.  To resolve the ambiguity, alias the “Monster.Useless.BadExamples” reference as:


using System.Data;
using MUBE = Monster.Useless.BadExamples;

...

    MUBE.DataColumn something = new MUBE.DataColumn();  // Uses Monster.Useless.BadExamples
    DataColumn builtInSomething = new DataColumn();     // Uses System.Data

Classes

Objects should be modeled after distinct entities which can have abilities (methods) and/or attributes (properties.)  Classes should be named using nouns (in Pascal case – words combined with first letter of each word capitalized) descriptive of the entity.   For example, the System.Collections namespace has classes with names such as ArrayList, HashTable, and Stack, all descriptive of the capabilities of each object.  Do not use the underscore character (“_”).

Classes should generally be organized into separate files, one file per class, named <classname>.cs.

Collections

  • Classes serving as collections should have names ending with “Collection”.  The general form when aggregating an entity class is <entityname>Collection. 

Example:
public class CustomerCollection {...} // Contains a collection of Customer instances

Attributes

  • Custom attributes must be named ending with “Attribute”.
     
Example:
public sealed class SampleAttribute : Attribute {...}

Interfaces

Interfaces should be named with a prefix of “I” followed by nouns, noun phrases or adjectives describing behavior and, like classes, in Pascal case.  For example, IEnumerable and ICollection.  Do not use underscores.

Like classes, each interface should be stored in a separate file named <interfacename>.cs.  (e.g. “ICollection.cs”.)

Variables

Variable Names

A variable name should use camel casing (all words combined without spaces, and the first letter of each word except the first word are capitalized) and should be as long as necessary to describe its purpose.  (e.g. “totalShippingAndHandling” and “userID”.)

·        Use the plural form of nouns when used in collections or arrays (e.g. string[] stateNames.)
·        See Abbreviations below for further naming guidelines.

Variable Naming Exceptions

It is accepted practice to use single letters (e.g. “i”, “j”, “k” …) for tightly-scoped variables, typically index or counter variables in loops, unless readability would be improved by using a longer, more descriptive name.

Often, an object instance may be named by the combination of the first letters of each word (or accepted abbreviation) of the class name.  For example, a StringBuilder might be represented by the variable “sb”.   Others might include “cn” for SqlConnection or “cmd” for SqlCommand objects.  Multiple objects of the same type in the same scope can be differentiated by appending a description to the root, for example, “cnSecurity” and “cnResume”.

Class Fields and Properties

To help enforce encapsulation, you should avoid exposing fields directly from a class.  Fields should be marked private (or protected) and exposed outside the class via properties, allowing control over how the variable is set.  A private field of a class should be named with a leading underscore (“_”) character.  If it is necessary to expose that variable publicly, use a property (get/set) with a Pascal case name.

Example:
private int _partID = DEFAULT_PART_ID;

public int PartID
{
       get {return _partID;}

       set
       {
               if (value < 1)
               {
                      throw new ArgumentOutOfRangeException(“PartID”, value, “Must be greater than zero”);
               }
               _partID = value;
       }
}

Parameters


Variables used in parameter lists (such as in constructors or methods) should use camel casing.

Example:
public string SomeMethod (int userID, string userName, string password)
{
       /// ...
}

Methods

Follow the same rules as Variable naming with the following additions:

  • Use Pascal casing (all words combined without spaces and first letter of each word capitalized)
  • The name should be as long as necessary to describe its purpose.
  • Should generally be composed of verbs or verb phrases (e.g. “CreateNameArray()”, “Persist()”, “GetUserPreferences()”.)




Abbreviations

  • Avoid the use of abbreviations in general, preferring to spell names out fully (e.g. use “GetWindow” instead of “GetWin” or “password” instead of “pwd” or “pass”.)
  • Use abbreviations only if they are generally well-known by developers (e.g. Guid, UI, Http)
  • Use abbreviations to shorten references to longer phrases (e.g. “UI” for “UserInterface”)
  • If you must use abbreviations, ensure they are consistent throughout the entire application.
  • Abbreviations longer than two characters should use mixed or lower case as appropriate in the current context (e.g. customHttpException, httpModule, GetHttpResult) otherwise, use all upper or lower case as appropriate (e.g. userID, idSource, ValidateCustomerID.)

Constants

Follow the same general rules as Variable naming with the following exceptions:

  • To aid readability and identification, the names of Constants should be UPPER_CASE with underscores (_) between words.

Example:
private const string DEFAULT_EMAIL_ADDRESS = “nobody@monster.com”;
public const int LOGIN_TIMEOUT_SECONDS = 45;

Enumerations

Follow the same general rules as Variable naming with the following exceptions:

  • Use Pascal case for enumeration types and value names.
  • Use abbreviations sparingly.
  • Do not use an Enum suffix on enumeration type names.
  • Use a singular name for most enumeration types, but use a plural name for enumeration types that are bit fields.

Example:
In,           
       Out,  
InOut
}




Regarding Hungarian Notation

It was common practice before .NET to use the variable naming convention known as Hungarian notation (e.g. arrstrStateNames,) but with .NET, the usefulness of Hungarian is minimized. 

From Inside C#, by Archer: “So why not simply continue using Hungarian notation?  Because Hungarian notation is useful in situations where it's beneficial to know the type, or scope, of a variable being used. However, […] all types in C# are objects and based on the .NET System.Object class. Therefore, all variables have a basic set of functionality and behavioral characteristics. For this reason, the need for Hungarian notation is lessened in the .NET environment.”

In addition, as all .NET development is standardized on VisualStudio.net, identification of the type of a given variable is merely a tool tip away.

The use of Hungarian notation is to be avoided.  This approach is standardized in the .NET Framework General Reference and numerous publications.

More Information

For more information on naming conventions in the .NET environment, see “Naming Guidelines” in the .NET Framework General Reference (which can be found through help in Visual Studio.NET.)

Comments

Popular posts from this blog

Cloud Computing in simple

How to Write an Effective Design Document

Bookmark