0% found this document useful (0 votes)
101 views

Coding Guidelines

This document provides coding guidelines for C# .NET projects. It covers topics such as formatting and style, documentation, naming conventions, project build configuration, and coding and design best practices. The guidelines are intended to promote consistency and readability in C# code. Sections include recommendations for indentation, spacing, comments, naming rules for different code elements, file structure, and object-oriented principles.

Uploaded by

Nenad Femic
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
101 views

Coding Guidelines

This document provides coding guidelines for C# .NET projects. It covers topics such as formatting and style, documentation, naming conventions, project build configuration, and coding and design best practices. The guidelines are intended to promote consistency and readability in C# code. Sections include recommendations for indentation, spacing, comments, naming rules for different code elements, file structure, and object-oriented principles.

Uploaded by

Nenad Femic
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 39

Coding Guidelines C# .

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

2 Formatting and Style ........................................................................................................................ 7


2.1 Wrapping ............................................................................................................................................. 7
2.2 Indentation .......................................................................................................................................... 7
2.3 Blank Lines .......................................................................................................................................... 8
2.4 Spacing ............................................................................................................................................... 8
2.5 Braces ................................................................................................................................................. 9

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

4 Naming Conventions ...................................................................................................................... 12


4.1 General Conventions ........................................................................................................................ 12
4.2 Rules for Identifiers ........................................................................................................................... 12
4.2.1 Assembly........................................................................................................................................... 12
4.2.2 File .................................................................................................................................................... 13
4.2.3 Namespace ....................................................................................................................................... 13
4.2.4 Interface ............................................................................................................................................ 13
4.2.5 Class and Struct ................................................................................................................................ 14
4.2.6 Member ............................................................................................................................................. 14
4.2.7 Method .............................................................................................................................................. 14
4.2.7.1 Extension Method ............................................................................................................................. 15
4.2.8 Public Constant ................................................................................................................................. 15
4.2.9 Property............................................................................................................................................. 15
4.2.10 Non-public Field and non-public Constant ........................................................................................ 15
4.2.11 Resource ........................................................................................................................................... 16
4.2.12 Parameter and Variable .................................................................................................................... 16
4.2.13 Enum Type and Value ...................................................................................................................... 16
4.2.14 Event ................................................................................................................................................. 16
4.2.15 Delegate ............................................................................................................................................ 17
4.2.16 Generic Type Parameter ................................................................................................................... 17
5 Project Build and Configuration .................................................................................................... 18
5.1 References ........................................................................................................................................ 18
5.2 Files and Structure ............................................................................................................................ 18
5.3 Build .................................................................................................................................................. 18

6 Coding and Design ......................................................................................................................... 19


6.1 Namespace ....................................................................................................................................... 19
6.2 Interface ............................................................................................................................................ 19
6.2.1 Interface Member .............................................................................................................................. 20
6.3 Visibility ............................................................................................................................................. 20
6.4 Struct ................................................................................................................................................. 20
6.4.1 Struct vs. Class ................................................................................................................................. 20
6.5 Class ................................................................................................................................................. 21
6.5.1 Static Class ....................................................................................................................................... 21
6.5.2 Abstract Class ................................................................................................................................... 21
6.5.3 Inheritance ........................................................................................................................................ 22
6.5.4 Constructor........................................................................................................................................ 22
6.5.5 Method .............................................................................................................................................. 23
6.5.5.1 Method Overloading .......................................................................................................................... 24
6.5.5.2 Extension method ............................................................................................................................. 24
6.5.6 Constant ............................................................................................................................................ 25
6.5.7 Property............................................................................................................................................. 26
6.5.8 Field .................................................................................................................................................. 26
6.6 Resource ........................................................................................................................................... 27
6.7 Variable ............................................................................................................................................. 27
6.7.1 Declaration and Initalization .............................................................................................................. 27
6.7.2 Cast ................................................................................................................................................... 28
6.8 Condition ........................................................................................................................................... 28
6.9 Statement .......................................................................................................................................... 29
6.9.1 Switch ................................................................................................................................................ 29
6.9.2 If - Else .............................................................................................................................................. 30
6.9.3 Loop .................................................................................................................................................. 30
6.10 Enum ................................................................................................................................................. 31
6.10.1 Flag Enum ......................................................................................................................................... 32
6.11 Event ................................................................................................................................................. 33
6.12 Delegate ............................................................................................................................................ 33
6.13 Generics ............................................................................................................................................ 34
6.14 Exceptions and Cleanup ................................................................................................................... 34
6.14.1 Exception Throwing .......................................................................................................................... 34
6.14.2 Exception Handling ........................................................................................................................... 35
6.14.3 Resource Cleanup ............................................................................................................................ 35
6.14.4 Try – Catch – Finally ......................................................................................................................... 35
6.15 LINQ .................................................................................................................................................. 36
6.16 String ................................................................................................................................................. 37
6.17 Collection .......................................................................................................................................... 37

7 Resources ........................................................................................................................................ 39
7.1 References ........................................................................................................................................ 39
7.2 Tools ................................................................................................................................................. 39
7.3 Useful links ........................................................................................................................................ 39

List of Tables

Table 1 Overview of changes ............................................................... Error! Bookmark not defined.


1 Overview
When talking about reusability we have to think about improving our overall code quality. We know that the
time pressure of our daily project business often stands in direct contradiction to that demand, but keep in
mind that poorly designed code causes higher effort for maintenance, extension and reuse.

Please note that these guidelines are nondogmatic work in progress and open for future changes!

1.1 Why are guidelines necessary?


Because not every developer

 is aware of the potential pitfalls of certain constructions in C#.


 is introduced into certain conventions when using the .NET Framework.
 is aware of the impact of using (or neglecting to use) particular solutions on aspects like security,
performance, multi-language support, etc.
 knows that not every developer is as capable in understanding an elegant, but abstract, solution as the
original developer.

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.

1.2 Basic Principles


We are trying to keep this document within a reasonable size, but that doesn’t mean that when something is
not mentioned in this guideline it must be okay.

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 format multi-line lambda statements like this (AV2400).


methodThatTakesAnAction.Do(x =>
{
// do something like this
}

 You should put the entire LINQ statement on one line, or start each keyword at the same indentation
(AV2400).

Single Line Format:


var query = from product in products where product.Price > 10 select product;

Multiple Line Format:


var query = from product in products
where product.Price > 10
select product;
 You should fit a method call on a single line (AIO 2.6).
If there are many arguments that don't fit on a line those can be wrapped, many per line or one per line. Put
the return type on the same line as method name.

Single Line Format:


result = DoSomeFunctionCall(param1, param2, param3);

Multiple Line Formats:


result = DoSomeFunctionCall(param1, param2, param3,
param4, param5);

result = DoSomeFunctionCall(
param1,
param2,
param3,
param4,
param5);

 You should wrap and indent complex conditions to improve readability.


Good:
if (isTrue
|| (someCondition && anotherCondition && thirdCondition)
|| nobodyCares)

if (isTrue
|| (someCondition
&& anotherCondition
&& thirdCondition)
|| nobodyCares)

Bad:
if (isTrue || (someCondition
&& anotherCondition) || nobodyCares)

2.3 Blank Lines


 You should add an empty line (AV2400)

 between two multi-line statements,

 between members,

 between unrelated code blocks,

 around the #region keyword,

 between the using statements of different companies.

 You should not add multiple blank lines.

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

3.1 Assembly Info


 You should properly fill the attributes of the AssemblyInfo.cs (AV2215).
[assembly: AssemblyCopyright("Copyright © Dzoni DzoniCom 2012")]
[assembly: AssemblyCompany("Dzoni DzoniCom")]
[assembly: AssemblyTitle("Dzoni.DZN.Common.System.Core")]
[assembly: AssemblyProduct("Dzoni.DZN.Common.System.Core")]

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).

 Do not use comments to repeat the code (AV2316).


Try to focus comments on the why and what of a code block and not the how. Avoid explaining the
statements in words, but instead help the reader understand why you chose a certain solution or algorithm
and what you are trying to achieve. If applicable, also mention that you chose an alternative solution
because you ran into a problem with the obvious solution.
Also keep in mind that self-explanatory code is always better than comments!
Good:
// Loop through each item in the wrinkles array, find the Wrinkle with
// the largest impact that is new, and store it in ‘bestWrinkle’.
for (int i = 0; i <= lastWrinkle; i++)
{
Wrinkle currentWrinkle = vectorWrinkles[i];
if (currentWrinkle.IsNew && maxImpact < currentWrinkle.Impact)
{
maxImpact = currentWrinkle.Impact;
bestWrinkle = currentWrinkle;
}
}

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
}
}

 You should write comments with the caller in mind (AV2306).


Write the documentation of your class with the class user and reusability of your code in mind. Assume the
user will not have access to the source code and try to explain how to get the most out of the functionality of
your class.

 You should write MSDN-style documentation (AV2307).


Following the MSDN on-line help style and word choice helps the developer to find its way through your
documentation more easily.
 You should use ‘//’ comments instead of ‘/* */’ for comments (AIO 2.11).
The single-line syntax (// …) is preferred even when a comment spans multiple lines.

 You should not comment-out code (AV1575).


Avoid to release code that is commented-out, add an explanation if you temporary do so. Nobody knows
what to do when they encounter a block of commented-out code. Was it temporarily disabled for testing
purposes? Was it copied as an example? Should I delete it? When?

3.2.1 Class and Member Comments


 Do use XML tags for documenting types and members (AV2305).
Document all public types and members of types using the built-in XML comment functionality of Visual
Studio. Documenting your code allows Visual Studio to pop-up the documentation when your class is used
somewhere else. Furthermore, by properly documenting your classes, Sandcastle can generate
professionally looking class documentation.

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.

3.2.2 TODO Comments


 You should not use TODO comments (AV2318).
Avoid releasing code containing TODO comments. Annotating a block of code or some work to be done
using a TODO or similar comment may seem a reasonable way of tracking work-to-be-done. But in reality,
nobody really searches for comments like that. Use a work item tracking system to keep track of left-overs.
4 Naming Conventions

4.1 General Conventions


 Do use proper US-English (AV1701).
All identifiers should be named using words from the American English language.

 Choose easily readable identifier names.


For example, HorizontalAlignment is more readable than AlignmentHorizontal.

 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.

 Identifiers that refer to an array or collection should have a plural name.

 Do not use terms like Enum, Class or Struct in a name (AV1707).

 Do not use underscores, hyphens, or any other non-alphanumeric characters (AIO 4.4.1).

 Do not include numbers in identifiers (AV1704).


Numbers in names of fields, variables or members are very rarely needed. In fact, in most cases they are a
lazy excuse for not defining a clear and intention-revealing name.

 Do not use Hungarian notation (AIO 4.4.3).

 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 Rules for Identifiers


The following section describes the capitalization and naming rules for different types of identifiers.

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

Naming Structure Name of the type it contains

Example List.cs // may contain class List<T>

 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

Naming Structure Noun

Example namespace Dzoni.DZN.Common.System

 Do name namespaces according a well-defined pattern (AV1725).


All namespaces should be named according to the pattern
Dzoni.DZN.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]

 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).

// Microsoft namespaces are first


using System;
using System.Collections;

// Then any other namespaces in alphabetic order


using Dzoni.DZN.Common.System;
using Dzoni.DZN.Common.System.Diagnostics;
using Dzoni.DZN.Common.UI;

4.2.4 Interface

Casing PascalCasing
‘I’ prefix

Naming Structure Noun


Example public interface IDictionary

4.2.5 Class and Struct

Casing PascalCasing

Naming Structure Noun, noun phrase or adjective phrase

Example Good:
class BusinessBinder
class SmartTextBox
class EditableCustomer

Bad:
// does not end with a noun, and does not explain its purpose
public class Common

// technically okay, but does not explain its purpose


public class SiteSecurity

 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

Naming Structure Verb or verb phrase

Example public void Print()


public void ProcessItem()

 Do name methods using a verb-object pair such as ShowDialog (AV1720).


A good name should give a hint on the what of a member, and if possible, the why. Also, don’t include And in
the name of the method. It implies that the method is doing more than one thing, which violates the single
responsibility principle.
interface IEmployeeRepository
{
Employee[] First() {} // Wrong: What does first mean? How many?
Employee[] GetFirstFive() {} // Better
Employee[] GetFiveMostRecent() {} // Best: self-describing
void Add(Employee employee) {} // Although not using verb-object pair;
// the type name is clear enough
}

4.2.7.1 Extension Method


 Do group extension methods in a class suffixed with Extensions (AV1745).
If the name of an extension method conflicts with another member or extension method, you must prefix the
call with the class name. Having them in a dedicated class with the Extensions suffix improves readability.

4.2.8 Public Constant

Casing PascalCasing for publicly visible


ALL capital only for abbreviation

Naming Structure Noun

Example public const string MessageText = "A";


public const double PI = 3.14159...;

4.2.9 Property

Casing PascalCasing

Naming Structure Noun, noun phrase or adjective phrase

Example public string CustomerName


public ItemCollection Items
public bool CanRead

 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”.

 Do name boolean properties with an affirmative phrase (CanSeek instead of CantSeek).

 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.

4.2.10 Non-public Field and non-public Constant

Casing camelCasing

Naming Structure Noun or Adjective


Example private string name;
private const string messageText = "B";

4.2.11 Resource

Casing PascalCasing

Naming Structure Noun

Example ArgumentExceptionInvalidName

 Do provide descriptive rather than short identifiers (AV2205).


Keep them concise where possible, but do not sacrifice readability for space.

 Do use only alphanumeric characters and underscores in naming resources (AV2205).

4.2.12 Parameter and Variable

Casing camelCasing

Naming Structure Noun

Example int customerID;

4.2.13 Enum Type and Value

Casing PascalCasing

Naming Structure Noun or Noun phrase

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

Naming Structure Verb or Verb phrase

Example // A close event that is raised after the window is closed.


public event EventHandler WindowClosed;

// A close event that is raised before a window is closed.


public event EventHandler WindowClosing;

 Do use -ing and -ed to express pre-events and post-events (AV1737).


Give event names a concept of before and after, using the present and past tense. For example,

 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;

// Occurs when the object needs to be deleted by the event handler.


public event EventHandler Delete;

// Occurs when the object is already deleted.


public event EventHandler Deleted;

4.2.15 Delegate

Casing PascalCasing

Naming Structure Verb or Verb phrase

Example public delegate WindowClosedEventHandler;

 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).

 Do not add the suffix Delegate to a delegate (AIO 4.4.2).

4.2.16 Generic Type Parameter

Casing PascalCasing
‘T’ prefix

Naming Structure Noun

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 prefix descriptive type parameter names with T (AIO 4.4.2).


TItem

 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.

<TPolicy> where TPolicy : Policy

5 Project Build and Configuration

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.

 Do not reference unnecessary assemblies (AIO 2.3).


This can improve build times, minimize chances for mistakes, and give readers a good impression.

 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.

5.2 Files and Structure


See also naming conventions for files.

 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.

 You should not suppress specific compiler warnings (AV2211).


6 Coding and Design

6.1 Namespace
See also naming conventions.

 Do use using statements instead of fully qualified type names (AV1510).


Limit usage of fully qualified type names to prevent name clashing. For example:
Bad:
var list = new System.Collections.Generic.List<string>();

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.

 An interface should be small and focused (AV1003).


Interfaces should have a clear name explaining the purpose or role of that interface within the system. Do
not combine many vaguely related members on the same interface, just because they were all on the same
class. Separate the members based on the responsibility of those members so that callers only need to call
or implement the interface related to a particular task. This rule is more commonly known as the Interface
Segregation Principle.

 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 can prevent bidirectional associations;


 They simplify the replacement of one implementation with another;

 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.2.1 Interface Member


 You should not implement interface members explicitly unless the members are intended to be called
only through the interface (AIO 4.9.7).
Explicitly implemented members can be confusing to developers because they don’t appear in the list of
public members and they can also cause unnecessary boxing of value types.

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.

 You should implement IEquatable<T> on value types (AIO 4.8).


The Object.Equals method on value types causes boxing and its default implementation is not very
efficient, as it uses reflection. IEquatable<T>.Equals can have much better performance and can be
implemented such that it will not cause boxing.

6.4.1 Struct vs. Class


 Do not define a struct unless the type has all of the following characteristics (AIO 4.8.1):

 It logically represents a single value, similar to primitive types (int, double, etc.).

 It has an instance size fewer than 16 bytes.


 It is immutable.

 It will not have to be boxed frequently.


In all other cases, you should define your types as classes instead of structs.

6.5 Class
See also naming conventions.

 A class or interface does have a single purpose (AV1000).


A class or interface should have a single purpose within the system it participates in. In general, a class is
either representing a primitive type like an email or ISBN number, an abstraction of some business concept,
a plain data structure or responsible for orchestrating the interaction between other classes. It is never a
combination of those. This rule is widely known as the Single Responsibility Principle, one of the S.O.L.I.D.
principles.

 You should not expose the objects an object depends on (AV1014). 


If you find yourself writing code like this then you might be violating the Law of Demeter:
someObject.SomeProperty.GetChild().Foo()

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 avoid bidirectional dependencies (AV1020).


Having bidirectional dependencies between classes means that two classes know about each other’s public
members or rely on each other’s internal behavior. Refactoring or replacing one of those two classes
requires changes on both parties and may involve a lot of unexpected work. The most obvious way of
breaking that dependency is introducing an interface for one of the classes and using dependency injection.

 Classes should have state and behavior (AV1025).


The only exception to this rule are classes that are used to transfer data over a communication channel, also
called Data Transfer Objects, or a class that wraps several parameters of a method. In general, if you find a
lot of data-only classes in your code base, you probably also have a few (static) classes with a lot of
behavior. Use the principles of object-orientation explained in this section and move the logic as close to the
data it applies to.

6.5.1 Static Class


 You should use static classes sparingly. Static classes should be used only as supporting classes for the
object-oriented core of the framework.

 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.2 Abstract Class


 Do not define public or protected-internal constructors in abstract types (AIO 4.9.10).
 Do define a protected or an internal constructor on abstract classes (AIO 4.9.10).
A protected constructor is more common and simply allows the base class to do its own initialization when
subtypes are created. An internal constructor can be used to limit concrete implementations of the abstract
class to the assembly defining the class.

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");
}
}

public class PocketBook : Book


{
public new void Print()
{
Console.WriteLine("Printing PocketBook");
}
}

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.

 Do not refer to derived classes from the base class (AV1013).


Having dependencies between a base class and its derivatives goes against proper object oriented design
and prevents other developers from adding new derived classes without having to modify 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 make sure that a method does only one thing (AV1115).


A method (or property) should do exactly one thing, and one thing only. Break down methods with too many
responsibilities in multiple small and focused methods with self-explaining names.

 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.

 Do not use parameters as temporary variables (AV1668).


Never use a parameter as a convenient variable for storing temporary state. Even though the type of your
temporary variable may be the same, the name usually does not reflect the purpose of the temporary
variable.

 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);
}

 You should not use ref or out parameters (AV1562).


Ref and out parameters make code less understandable and therefore may introduce bugs. Prefer
returning compound objects instead.

 You may avoid methods that take a Boolean flag (AV1564).


A flag parameter based on a bool is not self-explanatory. Consider the following method signature:
public Customer CreateCustomer(bool platinumLevel) {}

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)

 Do not arbitrarily vary parameter names in overloads (AIO 4.9.6).


If a parameter in one overload represents the same input as a parameter in another overload, the
parameters should have the same name. Parameters with the same name should appear in the same
position in all overloads.
Bad:
public void Write(string message, FileStream stream){}
public void Write(string line, FileStream file, bool closeStream){}

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;
}

public int IndexOf(string phrase)


{
return IndexOf(phrase, 0, someText.Length);
}

public int IndexOf(string phrase, int startIndex)


{
return IndexOf(phrase, startIndex, someText.Length - startIndex);
}

public virtual int IndexOf(string phrase, int startIndex, int count)


{
return someText.IndexOf(phrase, startIndex, count);
}
}

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.5.2 Extension method


See also naming conventions.
 Do not add extension methods to the same namespace as the extended class (AV1245).
Even though it may seem convenient to add extension methods related to the String class in the System
namespace, this may cause conflicts with future versions of the .NET Framework.

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);

public Color(int redGreenBlue)


{
// implementation
}
}

 Do not use "magic numbers" (AV1515).


Don’t use literal values, either numeric or strings, in your code other than to define symbolic constants.
public class Whatever
{
public static readonly Color PapayaWhip = new Color(0xFFEFD5);
public const int MaxNumberOfWheels = 18;
}

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.

 Do allow properties to be set in any order (AV1100).


Properties should be stateless with respect to other properties, i.e. there should not be a difference between
first setting property DataSource and then DataMember, and vice versa.

 Do not implement mutual exclusive properties (AV1110).


Having properties that cannot be used at the same time typically signals a type that is representing two
conflicting concepts. Even though those concepts may share some of the behavior and state, they obviously
have different rules that do not cooperate. This violation is often seen in domain models and introduces all
kinds of conditional logic related to those conflicting rules, causing a ripple effect that significantly worsens
the maintenance burden.

 You should not use a property but a method instead (AV1105).

 If the operation is orders of magnitude slower than setting a field value.


 If the operation is a conversion, such as the Object.ToString() method.

 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.

 Do not hardcode strings that change based on the deployment (AV2207).


Examples include connection strings, server addresses, etc. Use the own Configuration Management
System (CMS), the Resources, the ConnectionStrings property of the ConfigurationManager
class, or the Settings class generated by Visual Studio.

6.7 Variable
See also naming conventions for variables.

6.7.1 Declaration and Initalization


 Do declare local variables in the minimum scope block that can contain them, typically just before use;
otherwise, at the top of that scope block (AIO 2.5).

 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;

 Do use var only when the type is very obvious (AV1520).


Only use var as the result of a LINQ query, or if the type is very obvious from the same statement and
using it would improve readability.
Bad:
var i = 3; // what type? int? uint? float?
var myfoo = MyFactory.Create("arg"); // Not obvious what base-class or interface
// to expect. Also difficult to refactor if
// you can't search for the class.

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;

var countries = new List<string>();


countries.Add("Netherlands");
countries.Add("United States");

Good:
var startInfo = new ProcessStartInfo("myapp.exe")
{
StandardOutput = Console.Output,
UseShellExecute = true
};

var countries = new List<string> { "Irland", "Cuba" };

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)

 You should not use conditions with double negatives (AV1502).


Although a property like customer.HasNoOrders make sense, avoid using it in a negative condition like
this:
bool hasOrders = !customer.HasNoOrders;

Double negatives are more difficult to grasp than simple expressions.

 You should encapsulate complex conditions in a method or property (AV1547).


In particular if the conditions are used in more than one place.
Bad:
if (user != null
&& user.Answer != null
&& user.Answer.IsValid
&& (user.Answer.Value == "Yes"
|| user.Answer.Value == "Maybe"))
{
// do something
}

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
}

private bool IsInterested(User user)


{
return (user != null
&& user.Answer != null
&& user.Answer.IsValid
&& (user.Answer.Value == "Yes"
|| user.Answer.Value == "Maybe"));
}

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();

 You should not imprudently use multiple return statements (AV1540).


One entry, one exit is a sound principle and keeps control flow readable.

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.
}
}

 Do not use nested loops in a method (AV1532).


A method that nests loops is more difficult to understand than one with only a single loop. In fact, in most
cases nested loops can be replaced with a much simpler LINQ query that uses the from keyword twice or
more to join the data.
Good:
var query =
from person in persons
where person.IsInterested
from address in person.Address
select address.Street;

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).

 Do use an enum instead of a static constants or #define value (AIO 2.8).


If a variable can have a limited set of constant values, use an enumeration for defining the valid values.
Using the enumeration allows compile-time checking, prevents typos and gets some additional compiler and
reflection support.
Good:
public enum Color
{
None = 0,
Red,
Green,
Blue
}

Bad:
#define RED 0
#define GREEN 1
#define BLUE 2

public static class Color


{
public const int Red = 0;
public const int Green = 1;
public const int Blue = 2;
}

 Do not use an enum for open sets (such as, for example, the operating system version) (AIO 2.8).

 Do provide a value of zero on simple enums (AIO 2.8).


Consider calling the value something like None. If such value is not appropriate for this particular enum, the
most common default value for the enum should be assigned the underlying value of zero.
public enum Compression
{
None = 0,
GZip,
Deflate
}
 You may keep in mind that Enum.IsDefined loads reflection and a bunch of cold type metadata,
making it a surprisingly expensive call (AIO 2.8).

6.10.1 Flag Enum


Flag enums are designed to support bitwise operations on the enum values. See also naming conventions.

 Do not apply the System.FlagsAttribute to simple enums (AIO 2.8.1).

 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
}

SomeFlag flags = GetValue();


if ((flags & SomeFlag.ValueA) == SomeFlag.ValueA)
{
// Always evaluates to true!
}

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).

// Class that publishes an event


class Publisher
{
// Declare the event using EventHandler<T>.
// Tip: By assigning an empty delegate you can prevent
// the delegate list from being empty
public event EventHandler<CustomEventArgs> RaiseCustomEvent = delegate {};

public void DoSomething()


{
// Do something useful here and then raise the event.
// You can also raise an event before you execute a block of code.
OnRaiseCustomEvent(new CustomEventArgs("Did something"));
}

// Wrap event invocations inside a protected virtual method


// to allow derived classes to override the event invocation behavior.
protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;

// Event will be null if there are no subscribers


if (handler != null)
{
// Format the string to send inside the CustomEventArgs parameter.
e.Message += String.Format(" at {0}", DateTime.Now.ToString());

// Use the () operator to raise the event.


handler(this, e);
}
}
}

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.

 Do use generic constraints if applicable (AV1240).


Instead of casting to and from the object type in generic types or methods, use where constraints or the
as operator to specify the exact characteristics of the generic parameter. For example:

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;
}
}

6.14 Exceptions and Cleanup

6.14.1 Exception Throwing


 Do throw exceptions rather than returning some kind of status value or error code (AV1200).
A code base that uses return values for reporting the success or failure tends to have nested if-statements
sprinkled all over the code. Quite often, a caller forgets to check the return value anyhow. If a member
cannot successfully do what it is designed to do, it should be considered an execution failure and an
exception should be thrown.

 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 provide a rich and meaningful exception message text (AV1202).


The message should explain the cause of the exception and clearly describe what needs to be done to avoid
the exception.

 Do not explicitly throw exceptions from finally blocks (AIO 4.11.1).

6.14.2 Exception Handling


 Do not swallow errors by catching nonspecific exceptions, such as System.Exception (AV1210).
Do catch only specific errors that the code knows how to handle. Only top-level code, such as a Last-Chance
Exception Handler, should catch a non-specific exception for logging purposes and a graceful shutdown of
the application.

 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
}

6.14.3 Resource Cleanup


 Do not force garbage collections with GC.Collect()(AIO 4.12).

6.14.4 Try – Catch – Finally


 Do use try-finally blocks for cleanup and try-catch blocks for error recovery code (AIO 4.12.1).

 Do not use catch blocks for cleanup code (AIO 4.12.1).


Usually, the cleanup logic rolls back resource (particularly, native resource) allocations. For example,
FileStream stream = null;
try
{
stream = new FileStream(...);
...
}
finally
{
if (stream != null)
{
stream.Close();
}
}

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.

using (FileStream stream = new FileStream(...))


{
...
}

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>.

 Do not use read-only array fields (AIO 4.7).


The field itself is read-only and can’t be changed, but elements in the array can be changed.
This example demonstrates the pitfalls of using read-only array fields:
Bad:
public static readonly char[] InvalidPathChars = { '\"', '<', '>', '|'};
// callers cmay change the values in the array!
InvalidPathChars[0] = 'A'

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);
}

public static char[] GetInvalidPathChars()


{
return (char[])badChars.Clone();
}

 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.

 You should reconsider the use of Hashtable (AIO 4.7).


Instead, try other dictionary such as StringDictionary, NameValueCollection, HybridCollection.
Hashtable can be used if less number of values is stored.

 Do not return a null reference for Array or Collection (AIO 4.7).


Always return an empty array instead of a null reference. This also prevents cluttering your code base with
additional checks for null. For example, a user might assume that the following code will work,

int[] array = SomeOtherFunc();


foreach (int item in array)
{
...
}
7 Resources

7.1 References
This document mainly bases on the documents:

 AVIVA Coding Guidelines for C# 3.0 and 4.0 (AV)

 Microsoft All-In-One Code Framework Coding Guidelines (AIO)

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.

7.3 Useful links


We recommend the following books, articles and sites:

 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.

You might also like