Coding Practices
-
Avoid putting multiple classes in a single file.
-
A single file should contribute types to only a single namespace. Avoid having multiple namespaces in the same file.
-
Avoid files with more than 500 lines (excluding machine-generated code).
-
Avoid methods with more than 25 lines.
-
Avoid methods with more than five arguments. Use structures for passing multiple arguments.
-
Lines should not exceed 80 characters.
-
Do not manually edit any machine-generated code.
-
If modifying machine-generated code, modify the format and style to match this coding standard.
-
Use partial classes whenever possible to factor out the maintained portions.
-
Avoid comments that explain the obvious. Code should be self-explanatory. Good code with readable variable and method names should not require comments.
-
Document only operational assumptions, algorithm insights, and so on.
-
Avoid method-level documentation.
-
Use extensive external documentation for API documentation.
-
Use method-level comments only as tool tips for other developers.
-
With the exception of zero and one, never hardcode a numeric value; always declare a constant instead.
-
Use the const directive only on natural constants, such as the number of days of the week.
-
Avoid using const on read-only variables. For that, use the readonly directive:
public class MyClass { public const int DaysInWeek = 7; public readonlyint Number; public MyClass(int someValue) { Number = someValue; } }
-
Assert every assumption. On average, every fifth line is an assertion:
using System.Diagnostics;
object GetObject( ) {...}
object someObject = GetObject( ); Debug.Assert(someObject != null);
-
Every line of code should be walked through in a "white box" testing manner.
-
Catch only exceptions for which you have explicit handling.
-
In a catch statement that throws an exception, always throw the original exception (or another exception constructed from the original exception) to maintain the stack location of the original error:
catch(Exception exception) { MessageBox.Show(exception.Message); throw; //Same as throw exception; }
-
Avoid error code as method return values.
-
Avoid defining custom exception classes.
-
When defining custom exceptions:
-
Derive the custom exception from Exception.
-
Provide custom serialization.
-
Avoid multiple Main( ) methods in a single assembly.
-
Make only the most necessary types public; mark others as internal.
-
Avoid friend assemblies, as they increase interassembly coupling.
-
Avoid code that relies on an assembly running from a particular location.
-
Minimize code in application assemblies (i.e., EXE client assemblies). Use class libraries instead to contain business logic.
-
Avoid providing explicit values for enums:
//Correct public enum Color { Red,Green,Blue } //Avoid public enum Color { Red = 1,Green = 2,Blue = 3 }
-
Avoid specifying a type for an enum:
//Avoid public enum Color : long { Red,Green,Blue }
-
Always use a curly brace scope in an if statement, even if it contains a single statement.
-
Avoid using the trinary conditional operator.
-
Avoid function calls in Boolean conditional statements. Assign into local variables and check on them:
bool IsEverythingOK( ) {...} //Avoid: if(IsEverythingOK( )) {...} //Correct: bool ok = IsEverythingOK( ); if(ok) {...}
-
Always use zero-based arrays.
-
Always explicitly initialize an array of reference types:
public class MyClass {} const int ArrraySize = 100; MyClass[] array = new MyClass[ArrraySize]; for(int index = 0; index < array.Length; index++) { array[index] = new MyClass( ); }
-
Do not provide public or protected member variables. Use properties instead.
-
Avoid using the new inheritance qualifier. Use override instead.
-
Always mark public and protected methods as virtual in a non-sealed class.
-
Never use unsafe code, except when using interop.
-
Avoid explicit casting. Use the as operator to defensively cast to a type:
Dog dog = new GermanShepherd( ); GermanShepherd shepherd = dog asGermanShepherd; if(shepherd != null) {...}
-
Always check a delegate for null before invoking it.
-
Do not provide public event member variables. Use event accessors instead.
-
Avoid defining event-handling delegates. Use GenericEventHandler instead.
-
Avoid raising events explicitly. Use EventsHelper to publish events defensively.
-
-
Classes and interfaces should have at least a 2:1 ratio of methods to properties.
-
Avoid interfaces with one member.
-
Strive to have three to five members per interface.
-
Do not have more than 20 members per interface. The practical limit is probably 12.
-
Avoid events as interface members.
-
When using abstract classes, offer an interface as well.
-
Expose interfaces on class hierarchies.
-
Prefer using explicit interface implementation.
-
Never assume a type supports an interface. Defensively query for that interface:
SomeType obj1; IMyInterface obj2;
/* Some code to initialize obj1, then: */ obj2 = obj1 as IMyInterface; if(obj2 != null) { obj2.Method1( ); } else { //Handle error in expected interface }
-
Never hardcode strings that will be presented to end users. Use resources instead.
-
Never hardcode strings that might change based on deployment, such as connection strings.
-
Use String.Empty instead of "":
//Avoid string name = "";
//Correct string name = String.Empty;
-
When building a long string, use StringBuilder, not string.
-
Avoid providing methods on structures.
-
Parameterized constructors are encouraged.
-
You can overload operators.
-
Always provide a static constructor when providing static member variables.
-
Do not use late-binding invocation when early binding is possible.
-
Use application logging and tracing.
-
Never use goto, except in a switch statement fall-through.
-
Always have a default case in a switch statement that asserts:
int number = SomeMethod( ); switch(number) { case 1: Trace.WriteLine("Case 1:"); break; case 2: Trace.WriteLine("Case 2:"); break; default: Debug.Assert(false); break; }
-
Do not use the this reference unless invoking another constructor from within a constructor:
//Example of proper use of 'this' public class MyClass { public MyClass(string message) {} public MyClass( ) : this("Hello") {} }
-
Do not use the base word to access base class members unless you wish to resolve a conflict with a subclass member of the same name or when invoking a base class constructor:
//Example of proper use of 'base' public class Dog { public Dog(string name) {} virtual public void Bark(int howLong) {} } public class GermanShepherd : Dog { public GermanShepherd(string name) : base(name) {} override public void Bark(int howLong) { base.Bark(howLong); } }
-
Do not use GC.AddMemoryPressure( ).
-
Do not rely on HandleCollector.
-
Implement Dispose( ) and Finalize( ) methods based on the template in Chapter 4.
-
Always run code unchecked by default (for the sake of performance), but explicitly in checked mode for overflow- or underflow-prone operations:
int CalcPower(int number,int power) { int result = 1; for(int count = 1;count <= power;count++) { checked { result *= number; } } return result; }
-
Avoid explicit code exclusion of method calls (#if...#endif). Use conditional methods instead:
public class MyClass { [Conditional("MySpecialCondition")] public void MyMethod( ) {} }
-
Avoid casting to and from System.Object in code that uses generics. Use constraints or the as operator instead:
class SomeClass {} //Avoid: class MyClass<T> { void SomeMethod(T t) { object temp = t; SomeClass obj = (SomeClass)temp; } } //Correct: class MyClass<T> where T : SomeClass { void SomeMethod(T t) { SomeClass obj = t; } }
-
Do not define constraints in generic interfaces. Interface-level constraints can often be replaced by strong typing:
public class Customer {...} //Avoid: public interface IList<T> where T : Customer {...} //Correct: public interface ICustomerList : IList<Customer> {...}
-
Do not define method-specific constraints in interfaces.
-
If a class or a method offers both generic and non-generic flavors, always prefer using the generics flavor.
-
When implementing a generic interface that derived from an equivalent non-generic interface (such as IEnumerable<T>), use explicit interface implementation on all methods, and implement the non-generic methods by delegating to the generic ones:
class MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() {...} IEnumerator IEnumerable.GetEnumerator() { IEnumerable<T> enumerable = this; return enumerable.GetEnumerator(); } }
|