C# Coding Standards

C-sharpC# Coding Standards

General Naming Conventions

Following a consistent set of naming conventions can be a major contribution to readability. Beyond consistency of form, names of application and framework elements must be easily understood and must convey the functions of each element.

Word Choice

It is important that the names of identifiers make sense on the first reading. Identifier names should clearly state what each member does and what each type and parameter represents. To this end, it is more important that the name be clear than that it be short. Names should correspond to scenarios, logical or physical parts of the system, and well-known concepts rather than to technologies or architecture.

Generally:

Do choose easily readable identifier names. For example, a property named HorizontalAlignment is more readable in English than AlignmentHorizontal.
Do favour readability over brevity. The property name CanScrollHorizontally is better than ScrollableX (an obscure reference to the X-axis).

Casing

Do use camelCasing for local variables?

public class Foo {
 public void Bar() {
 string originalName = ...;
 Project newProject = ...;
 double width = ...;
 }
}

Do use camelCasing for parameters ?

public class Foo {
 public string MakeFullName(string firstName, string lastName) {
 ...
 }
}

Do use PascalCasing for class, function, property, delegate, event and enumeration value names?

class BitVector {
 public void Add(bool bit) { ... }
 public int Count { get { ... } }
 public override string ToString() { ... }
 
 public delegate void BitAddedEventHandler(bool value, int index);
 public event BitAddedEventHandler BitAdded;
 private enum Bit { Zero, One };
}

Naming

Do not prefix or suffix enums, classes, or delegates with any letter?

//Wrong

class CTask { // do not prefix with “C” – should be “Task”
 enum EKind { ... }; // do not prefix with “E” – should be “enum Kind”
 enum StateEnum { ... }; // do not suffix with “Enum” – should be “enum State”
 delegate string DFormatter(string s); // do not prefix with “D” – should be “Formatter”
 delegate int RoundDelegate(double d); // do not suffix with “Delegate” – should be “Round”
}
Do prefix interfaces names with “I” 
public interface IGraph {
 bool IsDirected { get; }
 bool AllowParallelEdges { get; set; }
 void Clear();
}

Do suffix attributes with “Attribute”?

public class ValidatorAttribute : System.Attribute {
 ...
}
Do suffix exceptions with “Exception”
public class UserNotFoundException : System.Exception {
 ...
}
Do not prefix local variables with “a”, “the” or similar
// Wrong
public class ... {
Project aProject = theTask.Project; // do not prefix with “a” – should be “project”
 } 
}

Do not use Hungarian notation (i.e., do not encode the type of a variable in its name) – unless the type is a GUI element, see below.

// Wrong

public class Foo {
 private int _nCount;
 private int _iLast;
 private bool _fDirty;
 private string _strName;
}

Do suffix GUI elements with the type name of the control.

Button _cancelButton
TextBox _nameTextBox

Do suffix classes inheriting from Form/Window/Page/Panel/<other GUI elements> with “Form”/”Window”/”Page”/”Panel”/”<other GUI elements>”

public partial class EditTaskWindow : System.Windows.Window {
 ...
}
Do suffix classes inheriting from UserControl with “Control”
public partial class IfdObjectBrowserControl : UserControl {
 ...
}
Do suffix DependencyProperties with “Property” [WPF]
public abstract class MyGuiElement: FrameworkElement {
 ...
 public static DependencyProperty TopLeftProperty =
 DependencyProperty.Register("TopLeft", typeof(Point),
 typeof(MyGuiElement), new PropertyMetadata(new Point(0, 0)));
...
}

Do not suffix class properties with “Property”

// Wrong

public class Task {
 string _name;
 public string NameProperty { get { ... } } // do not suffix with “Property” – should be “Name”
}

Do prefix boolean properties with “Is”, “Can”, “Has”, “Allow” or similar. The property name should preferably reflect a question that has a yes or no answer.

  • CanRead (not Readable)
  • IsPrintable (not Printable)
  • AllowParallelEdges (not ParallelEdges)
  • HandlesScrolling

That being said, consider testing out the common uses of the boolean property in an if statement. Such a usage test will highlight whether the word choices and grammar of the property name (e.g., active vs passive voice, singular vs plural) make sense as English phrases. For example, both of the following

if (collection.Contains(item))
if (regularExpression.Matches(text))

Read better than

if (collection.IsContained(item))
if (regularExpression.Match(text))


Also, all else being equal, prefer the active voice to the passive voice:
if (stream.CanSeek) // better than …
if (stream.IsSeekable)

Consider giving a property the same name as its type. For example, the following property correctly gets and sets an enum value named Status, so the property is named Status:

public enum Status { ... }
public class Task {
 public Status Status { get { ...} set { ... } }
}

Do name methods using verb-object pair
#ShowDialog not DialogShow
#PrintReport
#SaveFile

Do name methods after their behaviour
Do suffix event handlers with “_” and event name

public class EditTaskWindow : System.Windows.Window {
 Button _okButton;
public EditTaskWindow() {
 _okButton.Click += new RoutedEventHandler(OkButton_Click);
 }
void OkButton_Click(object sender, RoutedEventArgs e) {
 }
}

Comments

Comments should be used to describe the intent, algorithmic overview, and logical flow. It would be ideal, if from reading the comments alone, someone other than the author could understand the function’s behaviour and purpose. Although there are no minimum comment requirements and certainly some very small routines need no commenting at all, it is desirable for most routines to have comments reflecting the programmer’s intent and approach.

Do not use comments unless they describe something not obvious to someone other than the developer who wrote the code.
Avoid multiline syntax (/* … */) for comments. The single line syntax (// …) is preferred even when a comment spans multiple lines.

// Implements a variable size list that uses an array of objects
// to store the elements. A List has a capacity, which is the
// allocated length of the internal array. As elements are added
// to a List, the capacity of the List is automatically increased
// as required by reallocating the internal array.

public class List<T> : IList<T>, IList {
 ...
}
Do not place comments at the end of the line unless the comment is very short
public class ArrayList<T> {
 private int _count; // -1 indicates uninitialized array
 ...
}

Do use the /// comment to document public classes and methods

Abbreviations and acronyms

In general, do not use abbreviations or acronyms in public identifiers. As stated earlier, it is more important for names to be readable than brief. It is equally important not to use abbreviations and acronyms that are not generally understood – that is, the large majority of people who are not experts in a given field know immediately what they mean.

Do not use abbreviations or contractions as part of identifier names.

FindWindow not FindWin
Description not Desc
GetComment not GetComm

Do not abbreviate member variables

_number not _num
_description not _desc
_communicationDescriptor not _commDesc

Do not use any acronyms that are not widely accepted, and even if they are, only when necessary.

DepthFirstSearchAlgorithm not DfsAlgo
IDirectedAcyclicGraph not IDag
AdultOrientedRock not Aor
AreaOfResponsibility not Aor
UIElement is ok instead of UserInterfaceElement
HtmlTag is of course preferred over HyperTextMarkupLanguageTag

Consider using acronyms in local identifiers

foreach (AreaOfResponsibility aor in _areasOfResponsibility) {
 for (int i=0; i < aor.AssociatedContacts.Count ; ++i) {
 DepthFirstSearchAlgorithm dfs = new DepthFirstSearchAlgorithm(...);
 ...
 }
}

Do capitalize both characters of two-character acronyms except the first word of a camel-cased identifier.

System.IO not System.Io
public void StartIO(Stream ioStream)
not public void StartIo(…)
not public void StartIO(Stream IOStream)
not public void StartIO(Stream IoStream)

Do capitalize only the first character of acronyms with three or more characters except the first word of a camel-cased identifier.

System.Xml
public void ProcessHtmlTag(string htmlTag)

Generics

Do name generic type parameters with descriptive names, unless a single-letter name is completely self-explanatory and a descriptive name would not add value.

public interface ISessionChannel<TSession> { ... }
public delegate TOutput Converter<TInput,TOutput>(TInput from);
public class List<T> { ... }

Consider using T as the type parameter for types with one single-letter type parameter.

public int IComparer<T> { ... }
public delegate bool Predicate<T>(T item)
public struct Nullable<T> where T:struct { ... }
Do prefix descriptive type parameter names with T
public class HashTable<TKey, TValue> 
Consider indicating constraints placed on a type parameter in the name of the parameter.
// generic parameter is constrained to ISession, name the parameter TSession
public interface ISessionChannel<TSession> where TSession : ISession {
 TSession Session { get; }
}

Names of Namespaces

As with other naming guidelines, the goal of naming namespaces is clarity, so it will immediately be clear to the programmer making use of it what the content of the namespace is likely to be. The following template specifies the general rule for naming namespaces:
<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]

Do not use the same name for a namespace and a type in that namespace. For example, do not use Debug as a namespace name and then also provide a class named Debug in the same namespace.
Do not introduce generic type names such as Element, Node, Log and Message. There is a very high probability it would lead to type name conflicts in common scenarios. You should qualify the generic type names (FormElement, XmlNode, EventLog, SoapMessage).

File Organization

Consider not having more than one public type in a source file, unless they differ only in the number of generic parameters or one is nested in the other. Multiple internal types in one file are allowed.
Do name the source file with the name of the public type it contains.
Do name the assembly DLL file with the complete namespace qualified name.
Do use #region blocks around related pieces of code.
Consider not using nested #region blocks

General Style Conventions

Do use predefined types rather than the aliases in the System namespace. For instance
object not Object
string not String
int not Int32
Brace Usage

Do align opening and the closing braces with the beginning of the line (unless closing a single-statement – see below.)

if (someExpression)
{
 DoSomething();
 DoSomethingElse();
} // opening & closing braces are aligned with the “i” in “if”

Consider single statement blocks that have braces that begin and end on the same line. Property accessors often use this style.

public Task
{
 ...
 public string Name
 {
 get { return _name; }
 set { _name = value; }
 }
}

Consider single accessor properties having all the brackets on the same line.

public SkipList<T>
{
 ...
 public int Count { get { return _count; } }
}
Do place opening & closing braces for an 'if..else' statement in same line:
if (a > b)
{
 ...
} 
else
{
 ...
}
// Don't start 'else' on same line as closing brace of 'if':
if (a > b)
{
 ...
} else { // Wrong, refer above
 ...
}

Do always have braces, even on single-line if-statements; starting & ending can be on same line or different:

public bool HitTest(Point hitPoint)
{
 PathGeometry arrowHeadGeometry;
 PathGeometry lineGeometry;
 GetVisualEdgeGeometry(out lineGeometry, out 
 arrowHeadGeometry);
if (lineGeometry != null)
 {
 if (lineGeometry.StrokeContains(HitTestLinePen, 
 hitPoint))
 { return true; } // braces on same line
if (arrowHeadGeometry != null)
 {
if(arrowHeadGeometry.StrokeContains(HitTestLinePn, hitPoint))
 {
 return true;
 }
 if (arrowHeadGeometry.FillContains(hitPoint))
 {
 return true;
 }
 }
return false;
 }
}

Space Usage

Spaces improve readability by decreasing code density. Here are some guidelines for the use of space characters within code:
Do use a single space after a comma between function arguments.

Preferred: Console.In.Read(myChar, 0, 1);
Acceptable: Console.In.Read(myChar,0,1);

Avoid using spaces after the opening or before the closing paranthesis.

Preferred: CreateFoo(myChar, 0, 1)
Acceptable: CreateFoo( myChar, 0, 1 )

Avoid using spaces between a member name and opening parenthesis.

Preferred: CreateFoo(1, 2, 3)
Acceptable: CreateFoo (1, 2, 3)

Do not use spaces inside brackets.

Preferred: x = dataArray[index];
Acceptable: x = dataArray[ index ];

Do use a single space before flow control statements

Preferred: while (x == y)
Acceptable: while (x==y)

Do use a single space before and after comparison operators

Preferred: if (x == y)
Acceptable: if (x==y)

Do not use spaces before or after unary operators

Preferred: if (!ok)
Acceptable: if (! ok)

Indent Usage

Do use Visual Studio’s Smart Indent mode?
Do use 4 consecutive space characters for indents?

Very important to use above 2 VS standards to maintain uniformity.
Do indent contents of code blocks:

if (someExpression)
{
 DoSomething();
 DoSomethingElse();
}
Do indent case blocks even if not using braces
switch (someExpression)
{
 case 0:
 DoSomething();
 break;
 ....
}

Do break a statement if too long and indent the next line by 1 level. If the break-up runs upto more than 2 lines, all following lines should be at same level as 2nd line. Also do the break-up logically considering conditions/braces/assignments/method signatures etc.

If ((condition 1) && (condition 2) && (condition 3) && (condition 4) &&
(condition 5) && (condition 6) && (condition 7))
 {
 DoSomething();
 }
public int SomeFunction(int Parameter1, string Parameter2, string Parameter3,
 int Parameter4, int Parameter5, int Parameter6, string Parameter7,
 string Parameter8, int Parameter9)
 {
 DoSomethingAtLeastNow();
 }

SomeObject.SomeStringProperty = “Move this string down as it was too long to be kept on that first line.”;

Finalize and Dispose

Class instances often encapsulate control over resources not managed by the runtime (Hwnds,
Database connections etc). There needs to be both an explicit and an implicit way to free those
resources. The implicit control is provided by implementing the protected method Finalize() on Object.
The GC calls this method at some point after there are no longer any valid references to the object.
The explicit control is provided by a Dispose() method from the IDisposable interface. The consumer
of the object should call this method when he is done using the object and can be done even if other
references to the object are alive.

Resource classes should follow the pattern illustrated by this example:

class ResourceWrapper : IDisposable
{
private IntPrt handle; //pointer to a external resource
private OtherResource otherRes; //other resource you happen to use
private bool disposed = false;
public ResourceWrapper () {
handle = //allocate on the unmanaged side
otherRes = new OtherResource (…);
}
//free your own state
private void freeState () {
if (!disposed) {
CloseHandle (handle);
dispose = true;
}
}
//Free your own state, call dispose on all state you hold, and take
//yourself off the Finalization queue.
public void Dispose () {
freeState ();
OtherRes.Dispose();
GC.SuppressFinalization(this);
}
//Free your own state (NOT other state you hold) and give your
//parent a chance to finalize.
public void Finalize (){
freeState();
Base.Finalize();
}
//When ever you do something with this class, check to see if the
//state is disposed, if so, throw this exception.
public void DoStuff () {
if (disposed) {
throw ObjectDisposedException ("ResWrapper");
}
}
}

Finalize

  • Only implement Finalize on methods that need finalization. There is some cost associated with it.
  • Do not make the Finalize method more visible. It should NOT be public.
  • Do free any external resources you own in your the finalizer
  • If you only have managed references don’t implement the finalize method.
  • A finalizer should only release resources that are held onto by this object, and it should not reference any other objects.
  • Do not call a finalizer other than your base’s directly.
  • Do call base.Finalize() in your finalizer. Note: this is done automatically in with the C# and Visual
  • Basic destructor syntax.

Dispose

  • Do implement IDisposable
  • Do provide a public dispose method for users to free external resources
  • Do free any external resources in the dispose method
  • Do suppress finalization of your instance once Dispose has been called.
  • Do not assume that Dispose will be called. Resources should also be released in the finalizer just in
    case Dispose is not called.
  • Do throw an ObjectAlreadyDisposedException when resources are already disposed.
  • Do propagate dispose through containment hierarchies.
  • Dispose() should dispose of all resources held by this object and any object “owned” by this object

Exception Handling

  • Do not derive new exceptions from the base class Exception. The base class will have multiple subclasses according to individual namespace.
  • Do throw exceptions only in exceptional cases.
  • Do not use exceptions for normal or expected errors.
  • Do not use exceptions for normal flow of control.
  • Do throw an InvalidOperationException if in an inappropriate state.
  • The System.InvalidOperationException exception is supposed to be thrown if the property set or method call is not appropriate given the objects current state.
  • Do throw an ArgumentException or subclass there of if passed bad parameters.
  • When bad parameters are detected, throw a System.ArgumentException or a subclass there of.
  • Do realize that the stack trace starts at the throw.
  • Do throw the most specific exception possible
  • Do favour using existing exceptions over creating new ones
  • Do set all the fields on the exception you use
  • Do use Inner exceptions (chained exceptions).
  • Do set meaningful message text targeted at the developer in the exception.
  • Don’t have methods that throw NullReferenceException or IndexOutOfRangeException.

Wrapping Exceptions

Errors that occur at a layer below where your component sits should be wrapped in a meaningful
exception to your target users and thrown.

Leave a Reply

Your email address will not be published. Required fields are marked *