Coding Guidelines
Coding Guidelines
NET
1
Table of Contents
Page
1 Overview ............................................................................................................................................ 5
1.1 Why are guidelines necessary? .......................................................................................................... 5
1.2 Basic Principles ................................................................................................................................... 5
1.3 Terminology ........................................................................................................................................ 5
3 Documentation ................................................................................................................................ 10
3.1 Assembly Info.................................................................................................................................... 10
3.2 Comments ......................................................................................................................................... 10
3.2.1 Class and Member Comments ......................................................................................................... 11
3.2.2 TODO Comments ............................................................................................................................. 11
7 Resources ........................................................................................................................................ 39
7.1 References ........................................................................................................................................ 39
7.2 Tools ................................................................................................................................................. 39
7.3 Useful links ........................................................................................................................................ 39
List of Tables
Please note that these guidelines are nondogmatic work in progress and open for future changes!
Although complying with coding guidelines may seem to appear as undesired overhead or may limit
creativity, this approach has already proven its value for many years. Also beware of the fact that not all
coding guidelines have a clear rationale; some of them are simply choices we made.
Always refer back to a set of basic principles that apply to all situations, regardless of context. These include:
The Principle of Least Surprise (or Astonishment), which means that you should choose a solution that
does include any things people might not understand, or put on the wrong track.
Keep It Simple Stupid (a.k.a. KISS), a funny way of saying that the simplest solution is more than
sufficient.
You Ain’t Gonne Need It (a.k.a. YAGNI), which tells you to create a solution for the current problem
rather than the ones you think will happen later on (since when can you predict the future?)
Don’t Repeat Yourself (a.k.a. DRY), which requires you to rigorously remove duplication in your code
base.
Regardless of the elegancy of somebody’s solution, if it’s too complex for the average developer, or exposes
unusual behavior, or tries to solve many possible future issues, it is very likely the wrong solution and needs
redesign.
1.3 Terminology
Through-out this document there will be recommendations or suggestions for standards and practices. Some
practices are very important and must be followed, others are guidelines that are beneficial in certain
scenarios but are not applicable everywhere. In order to clearly state the intent of the standards and
practices that are discussed we will use the following terminology.
Wording Intent Justification
Do... This guideline should be followed in all cases. These standards are present to
Do Not... If you think that your specific application is mitigate bugs.
exempt, it probably isn't.
You should... This guideline is strongly recommended and These standards are typically
You should not... should be followed unless you have a stylistic and attempt to promote a
reasonable justification not to do so. consistent and clear style.
You may… This standard is recommended but may not be These standards are typically
applicable in all situations. stylistic, but are not ubiquitously
adopted.
2 Formatting and Style
Do not change the preconfigured Visual Studio Text Editor options.
2.1 Wrapping
You should limit the length of lines of code to 120 (use the script VS2008_guideline.reg to add a vertical
guideline to your Visual Studio Editor window). Break the code line when the line length is greater than the
specified column.
2.2 Indentation
Do use tabs (not spaces) for indentation.
You should format object initializers as follows and initialize each property on a new line (AV2400).
var dto = new Consumer()
{
Id = 123,
Name = "Microsoft",
PartnerShip = PartnerShip.Gold,
}
You should put the entire LINQ statement on one line, or start each keyword at the same indentation
(AV2400).
result = DoSomeFunctionCall(
param1,
param2,
param3,
param4,
param5);
if (isTrue
|| (someCondition
&& anotherCondition
&& thirdCondition)
|| nobodyCares)
Bad:
if (isTrue || (someCondition
&& anotherCondition) || nobodyCares)
between members,
2.4 Spacing
You should use spaces within a line as follows (AIO 2.9.2).
Good:
CreateFoo(); // No space between function name and parenthesis
Method(myChar, 0, 1); // Single space after a comma
x = array[index]; // No spaces inside brackets
while (x == y) // Single space before flow control statements
if (x == y) // Single space separates operators
sum = value1 + value2;
Bad:
CreateFoo (); // Space between function name and parenthesis
Method(myChar,0,1); // No spaces after commas
CreateFoo( myChar, 0, 1 ); // Space before first arg, after last arg
x = array[ index ]; // Spaces inside brackets
while(x == y) // No space before flow control statements
if (x==y) // No space separates operators
sum = value1+value2;
2.5 Braces
Do put opening and closing parentheses on a new line; statements within the braces are indented to the
next level (AIO 2.10).
Good:
if (x > 5)
{
y = 0;
}
Bad:
if (x > 5) {
y = 0;
}
You should use braces around single line conditionals (AIO 2.10).
Doing this makes it easier to add code to these conditionals in the future and avoids ambiguities should the
tabbing of the file become disturbed.
Good:
if (x > 5)
{
y = 0;
}
Bad:
if (x > 5)
y = 0;
3 Documentation
Note The attributes AssemblyVersion and AssemblyFileVersion are automatically overwritten by the Dzoni
Build Process.
3.2 Comments
Do write documentation in US English (AV2301).
Do indent comments at the same level as the code they describe (AIO 2.11).
Bad:
// Loop through each item in the wrinkles array
for (int i = 0; i <= lastWrinkle; i++)
{
Wrinkle currentWrinkle = vectorWrinkles[i]; // Get the next wrinkle
if (currentWrinkle.IsNew && // Process if it’s a new wrinkle
maxImpact < currentWrinkle.Impact) // And it has the biggest impact
{
maxImpact = currentWrinkle.Impact; // Save its impact for comparison
bestWrinkle = currentWrinkle; // Remember this wrinkle as well
}
}
For detailed information on XML documentation comments see the XML Documentation Comments Guide.
Tip The tool GhostDoc can generate a starting point for documenting code with a shortcut key.
Favor readability over brevity. The property name CanScrollHorizontally is better than
ScrollableX (an obscure reference to the X-axis).
Avoid using identifiers that conflict with keywords of widely used programming languages.
Do name an identifier according its meaning and not its type (AV1707).
Use semantically interesting names rather than language-specific keywords for type names. For
example, GetLength is a better name than GetInt.
Do not use underscores, hyphens, or any other non-alphanumeric characters (AIO 4.4.1).
You should not use abbreviations or acronyms as parts of identifier names (AV1706).
For example, use OnButtonClick rather than OnBtnClick. Avoid single character variable names, such
as i or q. Use index or query instead.
Exceptions Use well-known abbreviations that are widely accepted or well-known within the domain you
work. For instance, use UI instead of UserInterface.
4.2.1 Assembly
Do name an assembly after its contained namespace (AV1505).
As an example, consider a group of classes organized under the namespace Dzoni.DZN.Product
exposed by a certain assembly. That assembly should be called Dzoni.DZN.Product.dll.
Exception If you decide to combine classes from multiple unrelated namespaces into one assembly,
consider post fixing the assembly with Core, but do not use that suffix in the namespaces.
For instance, Dzoni.DZN.Common.System.Core.dll may contain classes organized under the
namespaces
Dzoni.DZN.Common.System,
Dzoni.DZN.Common.System.Security and
Dzoni.DZN.Common.System.Services.
4.2.2 File
Casing PascalCasing
Do name a source file to the logical function of the partial type (AV1508).
When using partial types and allocating a part per file, name each file after the logical part that part plays.
For example:
// In MyClass.cs
public partial class MyClass
{...}
// In MyClass.Designer.cs
public partial class MyClass
{...}
4.2.3 Namespace
Casing PascalCasing
Do not use the same name for a namespace and a type in that namespace (AV1725).
For example, do not use Debug for a namespace name and also provide a class named Debug in the same
namespace.
You should order and group namespaces according the company (AV2402).
4.2.4 Interface
Casing PascalCasing
‘I’ prefix
Casing PascalCasing
Example Good:
class BusinessBinder
class SmartTextBox
class EditableCustomer
Bad:
// does not end with a noun, and does not explain its purpose
public class Common
You should not include terms like Utility or Helper in classes (AV1708).
Classes with a name like that are usually static classes and are introduced without considering the object-
oriented principles.
4.2.6 Member
Do not repeat the name of a class or enumeration in its members (AV1710).
class Employee
{
Bad:
static GetEmployee() {}
DeleteEmployee() {}
Good:
static Get() {}
Delete() {}
}
You should name members similarly to members of .NET Framework classes (AV1711).
Stay close to the naming philosophy of the .NET Framework. Developers are already accustomed to the
naming patterns .NET Framework classes use, so following this same pattern helps them find their way in
your classes as well. For instance, if you define a class that behaves like a collection, provide members like
Add, Remove and Count instead of AddItem, Delete or NumberOfItems.
4.2.7 Method
Casing PascalCasing
4.2.9 Property
Casing PascalCasing
Do name collection properties with a plural phrase describing the items in the collection, as opposed to a
singular phrase followed by “List” or “Collection”.
You should consider prefixing boolean properties with Is, Has, Can, Allows, or Supports.
You should consider giving a property the same name as its type.
When you have a property that is strongly typed to an enumeration, the name of the property can be the
same as the name of the enumeration. For example, if you have an enumeration named CacheLevel, a
property that returns one of its values can also be named CacheLevel.
Casing camelCasing
4.2.11 Resource
Casing PascalCasing
Example ArgumentExceptionInvalidName
Casing camelCasing
Casing PascalCasing
Example [Flags]
public enum ConsoleModifiers
{
Alt,
Control
}
public enum Color
{
None = 0,
Red
}
Do name flag enums with plural nouns or noun phrases and simple enums with singular nouns or noun
phrases (AIO 4.4.2).
4.2.14 Event
Casing PascalCasing
Do not use Before or After prefixes or postfixes to indicate pre and post events (AIO 4.4.2).
For example, suppose you want to define events related to the deletion process of an object.
Bad:
public event EventHandler BeginDelete;
public event EventHandler EndDelete;
Good:
// Occurs just before the object is getting deleted.
public event EventHandler Deleting;
4.2.15 Delegate
Casing PascalCasing
Do add the suffix EventHandler to names of delegates that are used in events (AIO 4.4.2).
Do add the suffix Callback to names of delegates other than those used as event handlers (AIO 4.4.2).
Casing PascalCasing
‘T’ prefix
Example T
TItem
TPolicy
Do name generic type parameters with descriptive names, unless a single-letter name is completely self-
explanatory (AIO 4.4.2).
Do use T as the type parameter name for types with one single-letter type parameter (AIO 4.4.2).
T
You should consider indicating constraints on a type parameter in the name of parameter (AV1709).
For example, a parameter constrained to ISession may be called TSession.
5.1 References
Do always use relative paths to reference external assemblies.
Absolute paths or references to your local Global Assembly Cache (GAC) will prevent your project from
being successfully built on the Build Server. You can check the configuration of your references by opening
the .csproj file in an editor.
You may consider abstracting an external dependency or 3rd party component (AV1580).
If your code relies on some kind of external class, service or UI control, consider wrapping that dependency
in a lightweight wrapper that only exposes the members that are really used. Such a wrapper smoothens the
changes required when replacing that dependency with another, but can also be used to hide any undesired
behavior or bugs that you don’t have influence on.
You should not have 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 (AIO 4.2).
5.3 Build
You should not change the preconfigured Warning Level of the development environment.
Per default, Warning Level 4 is used.
6.1 Namespace
See also naming conventions.
Good:
using System.Collections.Generic;
var list = new List<string>();
If you do need to prevent name clashing, use a using directive to assign an alias:
using Label = System.Web.UI.WebControls.Label;
You should use C# type aliases instead of the types from the System namespace (AV2201).
These aliases have been introduced to make the primitive types a first class citizen of the C# language, so
use them accordingly,
Bad:
String message;
Int32 value;
Object anObject;
Good:
string message;
int32 value;
object anObject;
Exception When referring to static members of those types, it is common to use the full CLS name.
Bad:
int.Parse("666");
message = string.Empty
Good:
Int32.Parse("666");
message = String.Empty;
6.2 Interface
See also naming conventions.
You should use an interface to decouple classes from each other (AV1005).
Interfaces are a very effective mechanism for decoupling classes from each other and:
They allow replacing an expensive external service or resource with a temporary stub for use in a
non-production environment.
They allow replacing the actual implementation with a dummy implementation in a unit test;
Using a dependency injection framework you can centralize the choice which class is going to be
used whenever a specific interface is requested.
You may prefer an interface over a base class to support multiple implementations (AV1004).
If you want to expose an extension point to your class, expose it as an interface rather than a (abstract) base
class. It doesn’t force the users of that extension point to derive their implementations from a base-class that
might have undesired behavior. It improves testability and allows them to use their own implementation.
However, for their convenience you may implement an (abstract) default implementation that can serve as a
starting point.
6.3 Visibility
Do make all members private and types internal by default (AV1501).
To make a more conscious decision on which members to make available to other classes, explicitly set the
scope of all new members to private and that of a new type to internal. Then carefully decide what to
expose as a public member or type.
6.4 Struct
See also naming conventions.
Do ensure that a state where all instance data is uninitialized such as 0, false, or null (as appropriate)
is valid (AIO 4.8).
This prevents accidental creation of invalid instances when an array of the structs is created.
It logically represents a single value, similar to primitive types (int, double, etc.).
6.5 Class
See also naming conventions.
An object should not expose any other classes it depends on because callers may misuse that exposed
property or method to access the object behind it. By doing so, you allow calling code to become coupled to
the class you are using, and thereby limiting the chance you can easily replace it in a future stage.
You should mark classes that only contain static members as static (AV1008).
The advantage of using a static class is that the compiler can make sure that no instance members are
accidentally defined. The compiler will guarantee that instances of this class cannot be created and hence,
relieves you of creating a private constructor such as was required in C# 1.0. Use a static class to contain
methods that are not associated with a particular instance. For example,
public static class EuroConversion
{
public static decimal FromUSD(decimal inUsd) { ... }
public static decimal ToUSD(decimal inEuro) { ... }
}
6.5.3 Inheritance
You should prefer protected accessibility over public accessibility for virtual members (AIO 4.9.8).
Public members should provide extensibility (if required) by calling into a protected virtual member.
You should not hide inherited members with the new keyword (AV1010).
Not only does the new keyword break Polymorphism, one of the most essential object-orientation principles,
it also makes subclasses more difficult to understand. Consider the following two classes:
public class Book
{
public virtual void Print()
{
Console.WriteLine("Printing Book");
}
}
This will cause the following behavior which is not something you normally expect from class hierarchies.
PocketBook pocketBook = new PocketBook();
pocketBook.Print(); // Will output "Printing PocketBook"
((Book)pocketBook).Print(); // Will output "Printing Book"
It should not make a difference whether you call Print through the base class or through the derived class.
Do ensure that it is possible to treat a derived object as if it were a base class object (AV1011).
In other words, it should be possible to use a reference to an object of a derived class wherever a reference
to its base class object is used without knowing the specific derived class. A very notorious example of a
violation of this rule is throwing a NotImplementedException when overriding some of the base-class
methods. A less subtle example is not honoring the behavior expected by the base-class.
6.5.4 Constructor
Do explicitly declare the public default constructor in classes, if such a constructor is required (AIO 4.9.3).
Even though some compilers automatically add a default constructor to your class, adding it explicitly makes
code maintenance easier. It also ensures the default constructor remains defined even if the compiler stops
emitting it because you add a constructor that takes parameters.
Do not call virtual members on an object inside its constructors (AIO 4.9.3).
Calling a virtual member causes the most-derived override to be called regardless of whether the constructor
for the type that defines the most-derived override has been called.
You should not create a constructor that does not return a useful object (AV1001).
There should be no need to set additional properties before the object can be used for whatever purpose it
was designed. Anyway, do minimal work in the constructor. The cost of any other processing should be
delayed until required.
6.5.5 Method
See also naming conventions for methods and members.
Do order parameters on what will help programmers supply the right values.
For example, if a function takes arguments named “left” and “right”, put “left” before “right” so that their place
match their names.
You should validate arguments passed to public, protected, or explicitly implemented members.
For example,
using Dzoni.DZN.Common.System.Core;
public void DoSomething(string text, ICollection<int> collection)
{
ArgumentUtility.AssertNotNullNorEmpty("text", text);
ArgumentUtility.AssertNotNullNorEmpty("collection", collection);
}
On first sight this signature seems perfectly fine, but when calling this method you will lose this purpose
completely:
Customer customer = CreateCustomer(true);
Often, a method taking such a flag is doing more than one thing and needs to be refactored into two or more
methods. An alternative solution is to replace the flag with an enumeration.
6.5.5.1 Method Overloading
Do not use ref or out modifiers to overload members.
Bad:
public void Write(string message, int count)
public void Write(string message, out int count)
Good:
public void Write(string message, FileStream stream){}
public void Write(string message, FileStream stream, bool closeStream){}
You should call the most overloaded method from other overloads (AV1551).
This guideline only applies to overloads that are intended for providing optional arguments. For example,
public class MyString
{
private string someText;
public MyString(string text)
{
this.someText = text;
}
The class MyString provides three overloads for the IndexOf method, but two of them simply call the one
with the most arguments. Notice that the same rule applies to class constructors; implement the most
complete overload and call that one from the other overloads using the this() operator.
Important If you want to allow derived classes to override these methods, define the most complete
overload as a protected virtual method that is called by all overloads.
6.5.6 Constant
See also naming conventions for public constants and non-public constants and members.
Do use constant fields for constants that will never change. The compiler burns the values of const fields
directly into calling code. Therefore const values can never be changed without the risk of breaking
compatibility.
public class Int32
{
public const int MaxValue = 0x7fffffff;
public const int MinValue = unchecked((int)0x80000000);
}
Do use public static read-only fields for predefined object instances (AIO 4.5).
If there are predefined instances of the type, declare them as public static readonly fields of the type
itself.
public class Color
{
public static readonly Color Red = new Color(0xFF0000);
public static readonly Color Black = new Color(0x000000);
public static readonly Color White = new Color(0xFFFFFF);
Strings intended for logging or tracing are exempt from this rule. Literals are allowed when their meaning is
clear from the context, and not subject to future changes. For example,
mean = (a + b) / 2; // okay
WaitMilliseconds(waitTimeInSeconds * 1000); // clear enough
If the value of one constant depends on the value of another, do attempt to make this explicit in the code.
Bad:
public class SomeSpecialContainer
{
public const int MaxItems = 32;
public const int HighWaterMark = 24; // at 75%
}
Good:
public class SomeSpecialContainer
{
public const int MaxItems = 32;
public const int HighWaterMark = 3 * MaxItems / 4; // at 75%
}
Note An enumeration can often be used for certain types of symbolic constants.
6.5.7 Property
See also naming conventions for properties and members.
If the operation returns a different result each time it is called, even if the parameters didn’t change.
For example, the NewGuid() method returns a different value each time it is called.
If the operation causes a side effect such as changing some internal state not directly related the
property. Note that populating an internal cache or implementing lazy loading is a good exception.
If the property is set-only and a getter cannot be provided. The method name should begin with Set
followed by what would have been the property name.
You should not throw exceptions from property getters (AIO 4.9.2).
Property getters should be simple operations without any preconditions. If a getter might throw an exception,
consider redesigning the property to be a method. This recommendation does not apply to indexers.
Indexers can throw exceptions because of invalid arguments. It is valid and acceptable to throw exceptions
from a property setter.
You should replace properties using primitive types to use rich value objects (AV1140).
Instead of using strings, integers and decimals for representing domain specific types such as an ISBN
number, an email address or amount of money, consider created dedicated value objects that wrap both the
data and the validation rules that apply to it. By doing this, you prevent ending up having multiple
implementations of the same business rules, which both improves maintainability and prevents bugs.
6.5.8 Field
See also naming conventions for fields and members.
Do not provide instance fields that are public or protected (AIO 4.9.1).
Public and protected fields do not version well and are not protected by code access security demands.
Instead of using publicly visible fields, use private fields and expose them through properties.
You should not assign instances of mutable types to readonly fields (AIO 4.9.1).
The objects created using a mutable type can be modified after they are created. For example, arrays and
most collections are mutable types while Int32 and String are immutable types. For fields that hold a
mutable reference type, the readonly modifier prevents the field value from being overwritten but does not
protect the mutable type from modification. To avoid confusion, you should not use readonly properties for
mutable types.
6.6 Resource
See also naming conventions.
6.7 Variable
See also naming conventions for variables.
Do not declare multiple variables in a single line when using inline initialization.
Good:
CodeExample firstItem = null;
CodeExample secondeItem = null;
int a, b, c;
Bad:
CodeExample firstItem, secondItem = null;
Good:
var q = from order in orders where order.Items > 10 and order.TotalValue > 1000;
var repository = new RepositoryFactory.Get<IOrderRepository>();
var list = new ReadOnlyCollection<string>();
// In all of three above examples it is clear what type to expect.
You should not use selection statements instead of a simple assignment or initialization (AV1545).
Bad:
bool pos;
if (val > 0)
{
pos = true;
}
else
{
pos = false;
}
Good:
bool pos = (val > 0); // initialization
You should favor Object and Collection Initializers over separate statements (AV1523).
Bad:
var startInfo = new ProcessStartInfo("myapp.exe");
startInfo.StandardOutput = Console.Output;
startInfo.UseShellExecute = true;
Good:
var startInfo = new ProcessStartInfo("myapp.exe")
{
StandardOutput = Console.Output,
UseShellExecute = true
};
6.7.2 Cast
Do always check the result of an as operation (AV1570).
If you use as to obtain a certain interface reference from an object, always ensure that this operation does
not return null. Failure to do so may cause a NullReferenceException at a much later stage if the
object did not implement that interface.
6.8 Condition
Do not make explicit comparisons to true or false (AV1525).
Bad:
while (condition == false)
while (condition != true)
Good:
while (condition)
if (!condition)
In order to understand what this expression is about, you need to analyze its exact details and all the
possible outcomes. Obviously, you could add an explanatory comment on top of it, but it is much better to
replace this complex expression with a clearly named method:
Good:
if (IsInterested(user))
{
// do something
}
You still need to understand the expression if you are modifying it, but the calling code is now much easier to
grasp.
6.9 Statement
Do not put more than one statement on a single line because it makes stepping through the code in a
debugger much more difficult (AIO 2.7).
Good:
table.Rows.Add(row);
table.AcceptChanges();
Bad:
table.Rows.Add(row); table.AcceptChanges();
6.9.1 Switch
Do add a default block after the last case in a switch statement (AV1536).
Add a descriptive comment if the default block is supposed to be empty. Moreover, if that block is not
supposed to be reached throw an InvalidOperationException to detect future changes that may fall
through the existing cases. This ensures better code, because all paths the code can travel has been
thought about.
void Foo(string answer)
{
switch (answer)
{
case "no":
Console.WriteLine("You answered with No");
break;
case "yes":
Console.WriteLine("You answered with Yes");
break;
default:
// Not supposed to end up here.
throw new
InvalidOperationException("Unexpected: " + answer);
}
}
6.9.2 If - Else
Do finish every if-else-if statement with an else-part (AV1537).
The intention of this rule is the same as AV1536. For example,
void Foo(string answer)
{
if (answer == "no")
{
Console.WriteLine("You answered with No");
}
else if (answer == "yes")
{
Console.WriteLine("You answered with Yes");
}
else
{
// What should happen when this point is reached? Ignored? If not,
// throw an InvalidOperationException.
}
}
You should prefer conditional statements instead of simple if-else constructs (AV1546).
Bad:
if (someString != null)
{
result = someString;
}
else
{
result = "Unavailable";
}
return result;
Good:
return someString ?? "Unavailable";
6.9.3 Loop
Do not change a loop variable inside a for or foreach loop (AV1530).
Updating the loop variable within the loop body is generally considered confusing, even more so if the loop
variable is modified in more than one place. Although this rule also applies to foreach loops, an enumerator
will typically detect changes to the collection the foreach loop is iteration over.
Bad:
for (int index = 0; index < 10; ++index)
{
if (some condition)
{
index = 11; // Wrong! Use ‘break’ or ‘continue’ instead.
}
}
Bad:
var result = new List<string>();
foreach (var person in persons)
{
if (person.IsInterested)
{
foreach (var address in person.Address)
{
result.Add(address);
}
}
}
6.10 Enum
See also naming conventions.
Do use an enum to strongly type parameters, properties, and return values that represent sets of values
(AIO 2.8).
Bad:
#define RED 0
#define GREEN 1
#define BLUE 2
Do not use an enum for open sets (such as, for example, the operating system version) (AIO 2.8).
Do use powers of two for the flags enum values so they can be freely combined using the bitwise OR
operation. Do not mix up hexadecimal and decimal values (AIO 2.8.1).
[Flags]
public enum AttributeTargets
{
Assembly = 0x0001,
Class = 0x0002,
Struct = 0x0004
}
Do provide special enum values for commonly used combinations of flags (AIO 2.8.1).
Bitwise operations are an advanced concept and should not be required for simple tasks.
FileAccess.ReadWrite is an example of such a special value. However, you should not create flag
enums where certain combinations of values are invalid.
[Flags]
public enum FileAccess
{
Read = 0x1,
Write = 0x2,
ReadWrite = Read | Write
}
Do not use flag enum values of zero, unless the value represents “all flags are cleared” and is named
appropriately (AIO 2.8.1).
The following example shows a common implementation to determine if a flag is set (see the if-statement
below). The check works as expected for all flag enum values except the value of zero, where the boolean
expression always evaluates to true.
Bad:
[Flags]
public enum SomeFlag
{
ValueA = 0, // This might be confusing to users
ValueB = 1,
ValueC = 2,
ValueBAndC = ValueB | ValueC
}
Good:
[Flags]
public enum BorderStyle
{
None = 0x0
Fixed3D = 0x1,
FixedSingle = 0x2
}
BorderStyle foo = GetValue();
if (foo.BorderStyle == BorderStyle.None)
{
// Ok
}
6.11 Event
See also naming conventions.
Do implement events as shown in the following sample from MSDN (AV1220, AV1225).
Derived classes that override the protected virtual method are not required to call the base class
implementation. The base class must continue to work correctly even if its implementation is not called.
Do not pass null as the sender parameter when raising an event (AV1235).
Often, an event handler is used to handle similar events from multiple senders. The sender argument is then
used to get to the source of the event. Always pass a reference to the source (typically this) when raising
the event. Furthermore don’t pass null as the event data parameter when raising an event. If there is no
event data, pass EventArgs.Empty instead of null.
Exception On static events, the sender parameter should be null.
6.12 Delegate
See also naming conventions.
Do use Lambda expressions instead of delegates (AV2221).
Lambda expressions have been introduced in C# 3.0 and provide a much more elegant alternative for
anonymous delegates. So instead of
Bad:
Customer c = Array.Find(customers, delegate(Customer c)
{
return c.Name == "Tom";
});
Good:
Customer c = Array.Find(customers, c => c.Name == "Tom");
var customer = customers.Where(c => c.Name == "Tom");
6.13 Generics
See also naming conventions for generic type parameters.
class SomeClass {}
Bad:
class MyClass<T>
{
void SomeMethod(T t)
{
object temp = t;
SomeClass obj = (SomeClass) temp;
}
}
Good:
class MyClass<T> where T : SomeClass
{
void SomeMethod(T t)
{
SomeClass obj = t;
}
}
Do not use exceptions for the normal flow of control (AIO 4.11.1).
Except for system failures and operations with potential race conditions, you should write code that does not
throw exceptions. For example, you can check preconditions before calling a method that may fail and throw
exceptions. For example,
if (collection != null && !collection.IsReadOnly)
{
collection.Add(additionalNumber);
}
Do throw the most specific (the most derived) exception that is appropriate (AV1205).
For example, throw ArgumentNullException and not its base type ArgumentException if a null
argument is passed. Throwing System.Exception is nearly always the wrong thing to do.
Do prefer using an empty throw when catching and re-throwing an exception (AIO 4.11.2).
This is the best way to preserve the exception call stack.
Good:
try
{
// Do some reading with the file
}
catch
{
file.Position = position; // Unwind on failure
throw; // Rethrow
}
Bad:
try
{
... // Do some reading with the file
}
catch (Exception ex)
{
file.Position = position; // Unwind on failure
throw ex; // Rethrow
}
Many language constructs emit try-finally blocks automatically for you. Examples are using, lock and
foreach.
You should use the using statement instead of plain try-finally to clean up objects implementing
the IDisposable interface.
6.15 LINQ
Do evaluate the result of a LINQ expression before returning it (AV1250).
Consider the following code snippet
Bad:
public IEnumerable<Truck> GetTrucks()
{
const decimal TruckThresholdInKilogram = 3500;
var query = from vehicle in db.Vehicles
where vehicle.MPGVW > TruckThresholdInKilogram
select new Truck(vehicle.Nationality, vehicle.Lpn);
return query;
}
Since LINQ queries use deferred execution, returning query will actually return the expression tree
representing the above query. Each time the caller evaluates this result using a foreach or something
similar, the entire query is re-executed resulting in new instances of Truck every time. Consequently, you
cannot use the == operator to compare multiple Truck instances.
Instead, always explicitly evaluate the result of a LINQ query using ToList(), ToArray() or similar
methods.
Good:
public ICollection<Truck> GetTrucks()
{
const decimal TruckThresholdInKilogram = 3500;
var query = from vehicle in db.Vehicles
where vehicle.MPGVW > TruckThresholdInKilogram
select new Truck(vehicle.Nationality, vehicle.Lpn);
return query.ToArray();
}
You should consider using Any() to determine whether an IEnumerable<T> is empty (AV1700).
When a member returns an IEnumerable<T> or other collection class that does not expose a Count
property, use the Any() extension method rather than Count() to determine whether the collection
contains items. If you do use Count(), you risk that iterating over the entire collection might have a
significant impact (such as when it really is an IQueryable<T> to a persistent store).
Good:
if (vehicles.Any())
{
// do something
}
Bad:
if (vehicles.Count() > 0)
{
// do something
}
6.16 String
Do not use the ‘+’ operator to concatenate large number of strings (AIO 4.6).
Instead, you should use StringBuilder for concatenation.
You should not return a null reference but String.Empty for a string (AV1135).
Only return null for string references in case when the distinction between null and String.Empty is
significant. This prevents cluttering your code base with additional checks for null.
You should use overloads that explicitly specify the comparison rules for string operations (AIO 4.6).
Typically, this involves calling a method overload that has a parameter of type StringComparison. Use string
operations that are based on StringComparison.CurrentCulture when you display output to the user and
StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase when the comparison is linguistically
irrelevant (symbolic, for example).
6.17 Collection
Do return IEnumerable<T> or ICollection<T> instead of a concrete collection class (AV1130).
In general, you don’t want callers to be able to change an internal collection, so don’t return arrays, lists or
other collection classes directly. Instead, return an IEnumerable<T>, or, if the caller must be able to
determine the count, an ICollection<T>.
Instead, you can use either a ReadOnlyCollection<T> (only if the items are immutable) or clone the
array before returning it. (However, the cost of cloning the array may be prohibitive.)
Good:
public static ReadOnlyCollection<char> GetInvalidPathChars()
{
return Array.AsReadOnly(badChars);
}
You should prefer collections over arrays in public interfaces (AIO 4.7).
Use arrays in low-level functions to minimize memory consumption and maximize performance; access to
elements in an array is faster as it is optimized by the runtime.
You should reconsider the use of ArrayList (AIO 4.7).
Any objects added into the ArrayList are added as System.Object and when retrieving values back,
these objects are to be unboxed to return the actual value type. Use custom typed collections instead of
ArrayList, such as StringCollection in System.Collection.Specialized.
7.1 References
This document mainly bases on the documents:
7.2 Tools
Ghostdoc is a Visual Studio extension that automatically generates XML documentation comments for
methods and properties based on their type, parameters, name, and other contextual information.
For more information, see http://submain.com/products/ghostdoc.aspx.
Sandcastle produces accurate, MSDN style, comprehensive documentation by reflecting over the
source assemblies and optionally integrating XML Documentation Comments.
For more information, see http://sandcastle.codeplex.com/.
StyleCop analyzes C# source code to enforce a set of style and consistency rules. It can be run from
inside of Visual Studio or integrated into an MSBuild project. StyleCop has also been integrated into
many third-party development tools.
For more information, see http://stylecop.codeplex.com/.
FxCop is an application that analyzes managed code assemblies (code that targets the .NET
Framework common language runtime) and reports information about the assemblies, such as possible
design, localization, performance, and security improvements.
For more information, see http://msdn.microsoft.com/en-us/library/bb429476(VS.80).aspx.
Regionerate lets you define the areas in which it sticks code. They can be #regions, but they don't
have to be.
The Design Guidelines for Developing Class Libraries document on MSDN is a fairly thorough
discussion of how to write managed code.
LINQ Framework Design Guidelines is a set of rules and recommendations that you should adhere to
when using LINQ.
For detailed information on XML documentation of source code comments see the XML Documentation
Comments Guide.
Design patterns are recurring solutions to software design problems you find again and again in real-
world application development. You can find a nice tutorial on dofactor.
You can read Visual C# 2008 online in the openbooks section of http://www.galileocomputing.de.