Vous êtes sur la page 1sur 49

C# Coding Guidelines and Best Practices

Dot Net Development Open Book

C# Coding Guidelines and Best Practices

Pradeep Kumar Mishra

Pradeep Kumar Mishra Page 1


C# Coding Guidelines and Best Practices

1 INTRODUCTION ......................................................................................... 4
2 NAMING CONVENTIONS AND STYLE ................................................................. 4
2.1 Capitalization style ........................................................................... 4
2.1.1 Pascal case .................................................................................. 4
2.1.2 Camel case .................................................................................. 4
2.1.3 Uppercase ................................................................................... 4
2.1.4 Capitalization Summary ................................................................. 5
2.2 Naming rules................................................................................... 5
2.2.1 Namespaces ................................................................................ 6
2.2.2 Classes ....................................................................................... 6
2.2.3 Method ....................................................................................... 7
2.2.4 Interfaces.................................................................................... 7
2.2.5 Enums ........................................................................................ 8
2.2.6 Static Fields ................................................................................. 9
2.2.7 Variables ..................................................................................... 9
2.2.8 Properties .................................................................................. 10
2.2.9 Events ...................................................................................... 11
2.2.10 Case sensitivity ....................................................................... 12
2.2.11 General ................................................................................. 12
3 CODING STYLE ........................................................................................ 15
3.1 Source Files .................................................................................. 15
3.2 Ordering ....................................................................................... 15
3.3 Indentation ................................................................................... 15
3.3.1 Wrapping Lines .......................................................................... 15
3.3.2 White Spaces ............................................................................. 15
3.4 Comments .................................................................................... 16
3.4.1 Implementation Comments .......................................................... 16
3.4.2 Documentation Comments ........................................................... 18
3.4.3 Comment Tokens ........................................................................ 18
3.5 Formatting .................................................................................... 19
3.6 Declaration ................................................................................... 19
3.6.1 Number of Declarations per Line ................................................... 19
3.6.2 Initialization ............................................................................... 19
3.6.3 Class and Interface Declarations ................................................... 19
3.7 Statements ................................................................................... 20

Pradeep Kumar Mishra Page 2


C# Coding Guidelines and Best Practices

3.7.1 Simple Statements ..................................................................... 20


3.7.2 Return Statements ...................................................................... 20
3.7.3 If, if-else, if else-if else Statements ............................................... 20
3.7.4 For / Foreach Statements ............................................................. 21
3.7.5 While/do-while Statements........................................................... 22
3.7.6 Switch Statements ...................................................................... 22
3.7.7 Try-catch Statements .................................................................. 23
4 BEST PRACTICES ..................................................................................... 23
4.1 Performance ................................................................................. 24
4.2 Usage .......................................................................................... 28
4.3 Design ......................................................................................... 32
4.4 Globalization ................................................................................. 37
4.5 Security ....................................................................................... 38
4.6 Interoperability .............................................................................. 42
4.7 Exceptions .................................................................................... 44
5 PROJECT SETTINGS AND STRUCTURE............................................................. 46
6 REFERENCES: ......................................................................................... 48
APPNEDIX A ............................................................................................... 49

Pradeep Kumar Mishra Page 3


C# Coding Guidelines and Best Practices

1 Introduction
Quality has its own value in a software product development. The simplest method
for guaranteeing that a crew of the developers furnishes high quality deliverables is
to establish a coding standard. A comprehensive coding standard is essential for a
successful product delivery. Its not just the delivery but also the after delivery tasks
like support gets impacted by coding standards followed by the development team.
The standard helps in enforcing best practices and avoiding pitfalls and makes
knowledge dissemination across team easier.
A comprehensive coding standard encompasses all aspects of code construction and,
while developers should exercise prudence in its implementation, it should be closely
followed. Completed source code should reflect a harmonized style, as if a single
developer wrote the code in one session.

2 Naming conventions and style


2.1 Capitalization style
Use the following three conventions for capitalizing identifiers:

2.1.1 Pascal case


The first letter in the identifier and the first letter of each subsequent concatenated
word are capitalized. You can use Pascal case for identifiers of three or more
characters. For example: BackColor.

2.1.2 Camel case


The first letter of an identifier is lowercase and the first letter of each subsequent
concatenated word is capitalized. For example: backColor.

2.1.3 Uppercase
All letters in the identifier are capitalized. Use this convention only for identifiers that
consist of two or fewer letters. For example: System.IO, System.Web.UI.

Pradeep Kumar Mishra Page 4


C# Coding Guidelines and Best Practices

2.1.4 Capitalization Summary

Identifier Rules for Naming Notes/Examples


Class Pascal Case
Attribute Class Pascal Case Has a suffix of Attribute
Exception Class Pascal Case Has a suffix of Exception
Constant Pascal Case
Enum type Pascal Case
Enum values Pascal Case
Event Pascal Case
Interface Pascal Case Has a prefix of I
Local variable Camel Case
Method Pascal Case
Namespace Pascal Case
Property Pascal Case
Rarely used (use a
Public Instance Field Pascal Case
property instead)
Rarely used (use a
Protected Instance Field Camel Case
property instead)
Parameter Camel Case

2.2 Naming rules


Naming rules make programs more comprehensible by facilitating them for reading.
They can also give information about the function of the identifier, for example,
whether it's a constant, package, or class, which can be helpful in understanding the
code.
How the various elements of the application are named is perhaps the most
influential aids to understand the logical flow of an application is how the various
elements of the application are named. A name should tell "what" rather than "how."
By avoiding names that expose the underlying implementation, which can change,
you preserve a layer of abstraction that simplifies the complexity. For example, you
could use GetNextOrder() instead of GetNextArrayElement().
A tenet of naming is that difficulty in selecting a proper name may indicate that you
need to further analyze or define the purpose of an item. Let names be long enough
meaningfully however short enough avoid being wordy. Programmatically, a unique
name serves only to differentiate one item from another. Expressive names function
as an aid to the human reader; therefore, it makes sense to provide a name that the
human reader can comprehend. However, be certain that the names chosen are in
compliance with the applicable rules and standards.

Pradeep Kumar Mishra Page 5


C# Coding Guidelines and Best Practices

Most of the content of the naming rules has been taken from Microsoft
recommendations (link of the same has been given in the reference section).
There is a widely accepted code analyzer tool called FxCop (See reference section)
which also provide some guidelines. So if appropriate naming standards are not
defined in this document the FxCop Naming Rules should be used.

2.2.1 Namespaces
The general rule for namespace naming is:
[<Company Name>].[<Product | Technology Name>].[<Feature
Name>].[<SubNameSpace>]
Example:
Company.ProductName.Ecommerce.DataLayer;
Do avoid the possibility of two published namespaces having the same name.
Do not use department or project name as they change too frequently.
Do use Pascal Case and Nouns.
Do not use the underscore (_).
Do consider wider scope modules across an enterprise
Example: Company.ProductName.Core or Company.Core
Do use whole, simple and descriptive words and avoid acronyms and
abbreviations unless abbreviation is much more widely used than long form,
such as URL, Xml etc.
Do use plural namespace names where appropriate. For example, use
System.Collections rather than System.Collection. Exceptions to this rule are
brand names and abbreviations. For example, use System.IO not System.IOs.
Do not have namespaces and classes with the same name.

2.2.2 Classes
Do name classes with nouns or noun phrases using Pascal Case. Names may
start with an adjective, but should always end with a descriptive noun or noun
phrase (User, CustomUser or ReadonlyUser).
Do use sparingly, abbreviations in class names.
Do not use plural tense, use a second descriptive noun, or noun phrase
instead (UserCollection instead of Users)
Do not use any prefix (such as C, for example). Where possible, avoid
starting with the letter I, since that is the recommended prefix for interface
names. If you must start with that letter, make sure the second character is
lowercase, as in IdentityStore.
Do use suffixes at times. For example, if your system uses agents then
naming something DownloadAgent conveys real information.
Do not bring the name of the class that the class derives from into the
derived class's name (However there are some special cases mentioned
later). A class should stand on its own. It doesn't matter what it derives from.

Pradeep Kumar Mishra Page 6


C# Coding Guidelines and Best Practices

Do not use any underscores.


Do use suffix Exception with custom exception class.
e.g. public class NoDataFoundException{ .. }
Do use suffix Attribute with custom attribute class.
e.g public class PersistentEntityAttribute
Do add the suffix Stream to types that inherit from System.IO.Stream.

Example:
public class InvoiceItemGroup { }
public class Invoice { }

2.2.3 Method
Do name methods with Pascal Case
Do name methods with active verb forms and imperatives (ProcessInvoice,
SubmitTransaction). It is not necessary to include the noun name when the
active verb refers directly to the containing class.
Example:
Invoice.ProcessInvoice();
// AVOID! No need to mention Invoice in name
Invoice.Process(); // PREFER
Do Use the verb-noun method for naming routines that perform some
operation on a given object which describes the operation, such as
CalculateInvoiceTotal().
Do Not use elusive name like Analyze().

2.2.4 Interfaces
Do prefix interface names with the letter I, to indicate that the type is an
interface.
Do name interfaces with nouns or noun phrases, or adjectives describing
behavior (what the implementation will be.). For example, IComponent
(descriptive noun), ICustomAttributeProvider (noun phrase), and IPersistable
(adjective).
Do use Pascal Case.
Do use sparingly, abbreviations in interface names.
Do not use plural tense (IComponentCollection instead of IComponents)
Do not use any underscores.
Do use similar names when defining a class/interface pair where the class is a
standard implementation of the interface. The names should differ only by the

Pradeep Kumar Mishra Page 7


C# Coding Guidelines and Best Practices

I prefix in the interface name. This approach is used for the interface
IComponent and its standard implementation, Component.
Example:
public interface IComponent { }
public class Component : IComponent { }
public interface IServiceProvider{ }
public interface IFormatable { }

2.2.5 Enums
Do use Pascal Case for enums.
Do use Pascal Case for enum value names.
Do use sparingly, abbreviations in enum names.
Do not use a family-name prefix on enum.
Do not use any Enum suffix on enum types.
Do use a singular name for enums
Do use a plural name for enumerations with bit fields as values also called
flags enumerations.
SearchOptions(bitwise flags)
Do define enumerated values using an enum if they are used in a parameter
or property. This gives development tools a chance at knowing the possible
values for a property or parameter.
public enum TransactionType
{
Inquiry,
Sale,
Credit,
Authorization,
Capture,
Void
}
Do use the Flags custom attribute if the numeric values are meant to be
bitwise OR-ed together
[Flags]
public enum Bindings
{
CreateInstance,
DefaultBinding,
ExcatBinding,
GetField,
GetProperty,
IgnoreCase,
InvokeMethod,
NonPublic,
OABinding,
SetField

Pradeep Kumar Mishra Page 8


C# Coding Guidelines and Best Practices

SetProperty,
Static
}
Do use int as the underlying type of an enum. (An exception to this rule is if
the enum represents flags and there are more than 32 flags, or the enum
may grow to that many flags in the future, or the type needs to be different
from int for backward compatibility.)
Do use enums only if the value can be completely expressed as a set of bit
flags. Do not use enums for open sets (such as operating system version).

2.2.6 Static Fields


Do name static members with nouns, noun phrases, or abbreviations for
nouns.
Do name static members using Pascal Case.
Do not use Hungarian-type prefixes on static member names (Definition of
Hungarian notation is given in the Appendix A.)

2.2.7 Variables
Do use descriptive names such that a variables name that clearly indicates
what the purpose of the variable is and what value it will hold.
Do name variables using camel case.
Do initialize variables at the point of declaration if possible.
Do prefix private fields with an underscore (_) like _privateField. All other
variable must follow simply camel Case.
Do prefer names based on a parameters meaning, to names based on the
parameters type. It is likely that development tools will provide the
information about type in a convenient way, so the parameter name can be
put to better use describing semantics rather than type.
Do use the this keyword when referring to members at a classs root scope
from within a lower level scope, as in this.name.
Do not reserve parameters for future use. If more data is need in the next
version, a new overload can be added.
Do use computation qualifiers (avg, sum, min, max, index) as prefix and
suffix of a variable name where appropriate.
Do use is for boolean variable which implies Yes/No or True/False values,
such as isFound, or isSuccess.
Do name Collections as the plural form of the singular objects that the
collection contains. A collection of Book objects is named Books.
Do not using terms such as Flag when naming status variables, which differ
from Boolean variables in that they may have more than two possible values.
Instead of orderFlag, use a more descriptive name such as orderStatus.

Pradeep Kumar Mishra Page 9


C# Coding Guidelines and Best Practices

Do use a meaningful name even for a short-lived variable that may appear in
only a few lines of code. Use single-letter variable names, such as i or j for
short-loop indexes only.
Do not use all uppercase with underscores between words for constants, such
as NUM_DAYS_IN_WEEK. Constants follow the same naming rules as
properties. The aforementioned constant would be named NumDaysInWeek.
Do not use Hungarian-type prefixes. (Definition of Hungarian notation is given
in the Appendix A.)

Example:
public class Person
{
private int _birthYear;
public Person(int birthYear)
{
this._birthYear = birthYear;
}
public int BirthYear
{
get{ return this._birthYear; }
set{ this._birthYear = value; }
}
public string FormatBirthYear(int totalLength)
{
string birthYearText = this._birthYear.ToString();
int yearLength = birthYearText.length;

}
}

2.2.8 Properties
Do name properties using noun or noun phrases
Do name properties with Pascal Case
Do not include class names in the name of class properties, such as
Book.BookTitle. Instead, use Book.Title. This is the case with all object-
oriented languages.

Pradeep Kumar Mishra Page 10


C# Coding Guidelines and Best Practices

Do consider having a property with the same name as a type. When declaring
a property with the same name as a type, also make the type of the property
be that type. In other words, the following is okay
public enum Color {...}
public class Control
{
public Color Color { get {...} set {...} }
}
but this is not
public enum Color {...}
public class Control
{
public int Color { get {...} set {...} }
}
In the latter case, it will not be possible to refer to the members of the Color
enum because Color.Xxx will be interpreted as being a member access that
first gets the value of the Color property (of type int) and then accesses a
member of that value (which would have to be an instance member of
System.Int32).

2.2.9 Events
Do name event handlers with the EventHandler suffix.
public delegate void MouseEventHandler(object sender,
MouseEvent e);
Do use two parameters named sender and e. The sender parameter
represents the object that raised the event, and this parameter is always of
type object, even if it is possible to employ a more specific type. The state
associated with the event is encapsulated in an instance e of an event class.
Use an appropriate and specific event class for its type.
public delegate void MouseEventHandler(object sender,
MouseEvent e);
Do name event argument classes with the EventArgs suffix.
public class MouseEventArgs : EventArgs
{
int x;
int y;
public MouseEventArgs(int x, int y)
{ this.x = x; this.y = y; }
public int X { get { return x; } }
public int Y { get { return y; } }
}
Do Consider naming events with a verb. Use a gerund (the "ing" form of a
verb) to create an event name that expresses the concept of pre-event, and a
past-tense verb to represent post-event. For example, a Close event that can
be canceled should have a Closing event and a Closed event.

Pradeep Kumar Mishra Page 11


C# Coding Guidelines and Best Practices

Do Use an ing and ed form to express pre-events and postevents.


Do not use a pattern like BeginXxx and EndXxx. If you want to provide
distinct events for expressing a point of time before and a point of time after
a certain occurrence such as a validation event, do not use a pattern like
BeforeValidation and AfterValidation. Instead, use a Validating and Validated
pattern.
public event ControlEventHandler ControlAdded
{
//..
}

2.2.10 Case sensitivity


Dont use names that require case sensitivity. Components might need to be
usable from both case-sensitive and case-insensitive languages. Since case-
insensitive languages cannot distinguish between two names within the same
context that differ only by case, components must avoid this situation.
Examples of what not to do:
Dont have two namespaces whose names differ only by case.
namespace ee.cummings;
namespace Ee.Cummings;
Dont have a method with two parameters whose names differ only by case.
void F(string a, string A)
Dont have a namespace with two types whose names differ only by case.
System.WinForms.Point p;
System.WinForms.POINT p;
Dont have a type with two methods whose names differ only by case.
void f();
void F();

2.2.11 General
Always use US English.
When using Visual Studio.NET 2003 or Visual Studio.NET 2005 you can
specify code regions. Code regions are basically blocks of code that you can
collapse within the Visual Studio IDE. What code regions enable you to do is
group related pieces of code together which makes things easier to find. For
instance, I typically will group all my properties for a class within a Properties
code region.
#region P R O P E R T I E S

public string MyProp;

#endregion

Pradeep Kumar Mishra Page 12


C# Coding Guidelines and Best Practices

Please note that regions can be nested but they cant overlap the other block
statements.
Always use c# predefined types rather than the aliases in the System
namespace. For example
Use object and not Object
Use string and not String
Use int and not Int32
Avoid fully qualified name. Use the using statement instead.
All member variables should be declared at the top of a class definition.
public class MyClass
{
int _number
int _name;
public void SomeMethod()
}
Implementation details, in particular type specifications, should not be
mentioned in the name of a descriptor. This is a common trait in procedural
languages like Visual Basic where lowercase prefixes are used to encode the
data type in the name of the identifier, such as oInvoice. This approach is not
applicable to contemporary languages where the aforementioned identifier is
written simply as Invoice.
Names with semantic content are preferred to names with type specifications
(sizeOfArray instead of anInteger).
Minimize the use of abbreviations. If abbreviations are used, be consistent in
their use. An abbreviation should have only one meaning and likewise, each
abbreviated word should have only one abbreviation. For example, if using
min to abbreviate minimum, do so everywhere and do not later use it to
abbreviate minute.
When naming methods, include a description of the value being returned,
such as GetCurrentCustomerName().
Object references should be in camel case and they should be named after
their class. For example
InvoiceItemGroup invoiceItemGroup = new InvoiceItemGroup()
With the exception of zero and one, never hard code a numeric value; always
declare a constant instead. Please also note it is NOT ok to satisfy this
guideline by defining constants such as FortyTwo = 42.
Avoid using function calls in Boolean conditional statements. Assign into local
variable and check on them
bool ValidateInput()
{
.

Pradeep Kumar Mishra Page 13


C# Coding Guidelines and Best Practices

}
//Avoid
if(ValidateInput())
{
...
}
//Correct
bool isValidInput = ValidateInput();
if(isValidInput)
{
...
}

Do not use letters that can be mistaken for digits, and vice versa. To
create obfuscated code, use very short, meaningless names formed from
the letters O, o, l, I and the digits0 and 1. For example
bool b001 = (lo == l0) ? (I1 == 11) : (lOl != 101);
should not be written
Do not change a loop variable inside a for loop block. 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.
Do not make explicit comparisons to true or false.
It is usually bad style to compare a bool-type expression to true or false.
Example:
while (condition == false) // wrong; bad style
while (condition != true) // also wrong
while (((condition == true) == true) == true) // where do you
stop?
while (booleanCondition) // OK

Pradeep Kumar Mishra Page 14


C# Coding Guidelines and Best Practices

3 Coding Style
3.1 Source Files
Source files should contain one class per source file and name of file
should be same as class name. For example a file containing class Invoice
should have name Invoice.cs.
Consider breaking apart a file as it approaches 1500 lines.

3.2 Ordering
C# source files have the following ordering:
1. using statements
2. namespace statement
3. Class and interface declarations
Group all framework namespaces together and put custom or third party
namespaces underneath.
using System;
using System.Data;
using Company.ProductName.Core;
using Company.ProductName.Data;

3.3 Indentation
3.3.1 Wrapping Lines
When an expression will not fit on a single line, break it up according to these
general principles:
Break after a comma.
Break after an operator.
Prefer higher-level breaks to lower-level breaks.
Align the new line with the beginning of the expression at the same level on
the previous line
A good coding practice is to make the tab and space chars visible in the editor which
is used.
Example
SomeMethod(longExpresssion1, longExpression2,
longExpression3)

3.3.2 White Spaces


Always use 4 spaces for indentation. Since we use Visual studio as IDE hence its
better to use tabs over there. (By default tab is set to 4 spaces and this will reduce
typing.)

Pradeep Kumar Mishra Page 15


C# Coding Guidelines and Best Practices

3.4 Comments
C# programs can have two kinds of comments: implementation comments and
documentation comments. Implementation comments are those found in C++, which
are delimited by /*...*/, and //. Documentation comments are C# only, and are
delimited by special XML tags that can be extracted to external files for use in
system documentation.
Implementation comments are meant for commenting out code or for comments
about the particular implementation. Doc comments are meant to describe the
specification of the code, from an implementation-free perspective, to be read by
developers who might not necessarily have the source code at hand.
Comments should be used to give overviews of code and provide additional
information that is not readily available in the code itself.
Consider an example below.
if ( grandTotal >= 1000.00)
{
grandTotal = grandTotal * 0.90;
}
Looking at the code in example above one can figure out that a 10% discount is
being given on orders of $1,000 dollars or more. Why is this being done? Is there a
business rule that says that large orders get a discount? Is there a limited-time
special on large orders or is it a permanent program? Was the original programmer
just being generous? I do not know unless it is documented somewhere, either in the
source code itself or in an external document
Comments should contain only information that is relevant to reading and
understanding the program.
Following are recommended commenting techniques:
When modifying code, always keep the commenting around it up to date.
Comments should consist of complete sentences and follow active language
naming responsibilities (Adds the element instead of The element is added).
At the beginning of every routine, XML documentation is used to indicate the
routine's purpose, assumptions, and limitations. A boilerplate comment should be a
brief introduction to understand why the routine exists and what it can do.
Avoid adding comments at the end of a line of code; end-line comments make
code more difficult to read. However, end-line comments are appropriate when
annotating variable declarations. In this case, align all end-line comments at a
common tab stop.
Comment anything that is not readily obvious in the code. This point leads to
allot of subjective interpretations. Use your best judgment to determine an
appropriate level of what it means for code to be not really obvious.
Throughout the application, construct comments using a uniform style, with
consistent punctuation and structure.

3.4.1 Implementation Comments


Block Comments

Pradeep Kumar Mishra Page 16


C# Coding Guidelines and Best Practices

Block comments are used to provide descriptions of files, methods, data


structures and algorithms. Block comments may be used at the beginning of each
file. They can also be used in other places, such as within methods. Block
comments inside a function or method should be indented to the same level as
the code they describe.
A blank line to set it apart from the rest of the code should precede a block
comment.
// Here is a block comment
// that breaks across multiple
// lines.

Single-Line Comments
Short comments can appear on a single line indented to the level of the code that
follows. If a comment can't be written in a single line, it should follow the block
comment format. A single-line comment should be preceded by a blank line.
Here's an example of a single-line comment in code.
if (condition)
{
// Handle the condition.
...
}
Trailing Comments
Very short comments can appear on the same line as the code they describe, but
should be shifted far enough to separate them from the statements. If more than
one short comment appears in a chunk of code, they should all be indented to
the same tab setting.
Here's an example of a trailing comment in C# code:
ss = s1 + s2 + s3; //add the three scores into the sum
a = float(ss) / x; //calculate the mean of the scores

Code-Disabling Comments
The // comment delimiter can comment out a complete line or only a partial line.
Code-disabling comment delimiters are found in the first position of a line of code
flush with the left margin. Visual Studio .NET provides for bulk commenting by
selecting the lines of code to disable and pressing CTRL+K, CTRL+C. To
uncomment, use the CTRL+K, CTRL+U chord.
We should try not to check in the commented code as that may confuse other
developers working on the same code.(Unless there is very specific reason. Code
Reviewer should check this.)

The following is an example of code-disabling comments:


if (foo > 1)
{
// Do a double-flip.
...
}
else
{
return false; // Explain why here.
}
// if (bar > 1)

Pradeep Kumar Mishra Page 17


C# Coding Guidelines and Best Practices

// {
//
// // Do a triple-flip.
// ...
// }
// else
// {
// return false;
// }

3.4.2 Documentation Comments


C# provides a mechanism for developers to document their code using XML. XML
documentation starts with ///.
Consider following points while writing XML documentation
The documentation must be well-formed XML. If the XML is not well-formed, a
warning is generated and the documentation file will contain a comment
saying that an error was encountered.
Developers are not free to create their own set of tags.
There is a recommended set of tags.
Some of the recommended tags have special meanings:
o The <param> tag is used to describe parameters. If used, the
compiler will verify that the parameter exists and that all parameters
are described in the documentation. If the verification failed, the
compiler issues a warning.
o The cref attribute can be attached to any tag to provide a reference to
a code element. The compiler will verify that this code element exists.
If the verification failed, the compiler issues a warning. The compiler
also respects any using statements when looking for a type described
in the cref attribute.
o The <summary> tag is used by IntelliSense inside Visual Studio to
display additional information about a type or member.
If you need to give information about a class, interface, variable, or method that isn't
appropriate for documentation, use an implementation block comment or single-line
comment immediately after the declaration.
Document comments must not be positioned inside a method or constructor
definition block, because C# associates documentation comments with the first
declaration after the comment.

3.4.3 Comment Tokens


When you add comments with comment tokens to your code, you automatically add
shortcuts to the Task List window. Double-click any comment displayed in the Task
List to move the insertion point directly to the line of code where the comment
begins.
Note: Comments in HTML, .CSS, and .XML markup are not displayed in the Task
List.

Pradeep Kumar Mishra Page 18


C# Coding Guidelines and Best Practices

To add a comment hyperlink to the Task List window, enter the comment marker.
Enter TODO, HACK, or UNDONE. Add the Comment text.
// ToDo: Fix this later
// Hack: Temporary solution to be enhanced later

3.5 Formatting
Visual studio 2005 has an auto formatting feature and overall we should follow its
style and rules.

3.6 Declaration
3.6.1 Number of Declarations per Line
One declaration per line is recommended since it encourages commenting1. In other
words,
int level;
int size;
Do not put more than one variable or variables of different types on the same line
when declaring them.

3.6.2 Initialization
Try to initialize local variables as soon as they are declared.
string name = myObject.Name;
or
int val = time.Hours;
Note: If you initialize a dialog try to use the using statement:
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
...
}

3.6.3 Class and Interface Declarations


When coding C# classes and interfaces, the following formatting rules should be
followed:
No space between a method name and the parenthesis "(" starting its
parameter list.
The opening brace "{" appears in the next line after the declaration
statement.
The closing brace " }" starts a line by itself indented to match its
corresponding opening brace.

Pradeep Kumar Mishra Page 19


C# Coding Guidelines and Best Practices

This is the formatting followed by VS2005 auto formatter.


For example :
class MySample : MyClass, IMyInterface
{
int myInt;
public MySample(int myInt)
{
this.myInt = myInt ;
}
void Inc()
{
++myInt;
}
void EmptyMethod()
{
}
}

3.7 Statements
3.7.1 Simple Statements
Each line should contain only one statement.

3.7.2 Return Statements


A return statement should not use outer most parentheses.
Don't use : return (n * (n + 1) / 2);
use : return n * (n + 1) / 2;

3.7.3 If, if-else, if else-if else Statements


if, if-else and if else-if else statements should look like this:
if (condition)
{
DoSomething();
...
}
if (condition)

Pradeep Kumar Mishra Page 20


C# Coding Guidelines and Best Practices

{
DoSomething();
...
}
else
{
DoSomethingOther();
...
}
if (condition)
{
DoSomething();
...
}
else if (condition)
{
DoSomethingOther();
...
}
else
{
DoSomethingOtherAgain();
...
}
Note: Always use brackets even if there is only one statement in the loop.
E.g. if(connection!=null)
{
Connection.close();
}

3.7.4 For / Foreach Statements


A for statement should have following form:
for (int i = 0; i < 5; ++i)
{
...
}

Pradeep Kumar Mishra Page 21


C# Coding Guidelines and Best Practices

A foreach should look like :


foreach (int i in IntList)
{
...
}

3.7.5 While/do-while Statements


A while statement should be written as follows:
while (condition)
{
...
}

An empty while should have the following form:


while (condition) ;

A do-while statement should have the following form:


do
{
...
} while (condition);

3.7.6 Switch Statements


Always use default case with switch statement. A switch statement should be of
following form
switch (condition)
{
case A:
...
break;
case B:
...
break;
default:
...
break;

Pradeep Kumar Mishra Page 22


C# Coding Guidelines and Best Practices

3.7.7 Try-catch Statements


A try-catch statement should follow this form:
try
{
...
}
catch (Exception e)
{
}

or
try
{
...
}
catch (Exception e)
{
...
}
finally
{
...
}

4 Best Practices
This section discuss about the best practices that should be followed while writing
code in c#. The section is divided in subsection based on Performance, Usage,
Design, Globalization, Security, Interoperability and Exceptions.
Most of the content of this section has been taken as it is from
http://www.codeproject.com and FxCop (http://www.gotdotnet.com/team/fxcop/)
rules.

Pradeep Kumar Mishra Page 23


C# Coding Guidelines and Best Practices

4.1 Performance
Prefer the Length property when checking string size
String comparison involves unnecessary overhead. If all you need is to check
whether the string is empty, use the Length property.
Code snippets:
//NO
if ( str != )//comparison of two strings
{
...
}
//YES
if ( str.Length > 0)
{
...
}
Prefer StringBuilder instead of string concatenation.
C# string is immutable, i.e., cannot be altered. When you alter a string, you
are actually creating a new string causing the following:
The code uses more memory than necessary.
Creates more work for the garbage collector.
Makes the code execution run slower.
Therefore, you should prefer using StringBuilder (Append method).

Code snippets:

//NO
string strConcat;
ArrayList arrayOfStrings = new ArrayList();
arrayOfStrings.Add("a");
arrayOfStrings.Add("b");
foreach (string s in stringContainer) {
strConcat += s;
}

//YES
StringBuilder sbConcat = new StringBuilder ();
foreach (string s in arrayOfStrings ) {
sbConcat.append(s);
}

For very simple concatenation we should directly add them as


follows:

//Yes
return a + b + c;

//NO
StringBuilder a = new StringBuilder();

Pradeep Kumar Mishra Page 24


C# Coding Guidelines and Best Practices

a.Append(b);
a.Append(c);
return a;

Avoid unsealed attributes.


The .NET Framework class library provides methods for retrieving custom
attributes. By default, these methods search the attribute inheritance
hierarchy; for example System.Attribute.GetCustomAttribute searches for the
specified attribute type, or any attribute type that extends the specified
attribute type. Sealing the attribute eliminates the search through the
inheritance hierarchy, and can improve performance.
Example:
using System;

namespace PerformanceLibrary
{
//AvoidUnsealedAttributes.

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct)]
public sealed class DeveloperAttribute: Attribute
{
private string nameValue;
public DeveloperAttribute(string name)
{
nameValue = name;
}

public string Name


{
get
{
return nameValue;
}
}
}

Avoid Boxing and UnBoxing as much as possible.


Boxing a value of a value-type consists of two operations:
1. Allocating an object instance.
2. Copying the value-type value into that instance.
Given the above, you should avoid Boxing as much as you can. If you intend to
work with ArrayList, for example, do not declare your data type as struct (Value
type) because ArrayList works with Object (Reference type) and every time you
add an instance of the struct or run over the container, in a loop, a Boxing
process will occur.
The following happens when you copy one of the collection items value into the
struct:

Pradeep Kumar Mishra Page 25


C# Coding Guidelines and Best Practices

Casting.
Copy the value.
Note: Collections expect object type.

Code snippets:

//NO
struct st { public Int i; }
Arraylist arr = new ArrayList();
for (int i=0 ; i< count; i++)
{
st s;
s.i = 4;
arr.item.add(st) ; //<- Boxing (Allocating an object instance
// + copying the value-type value into that instance)
}
st obj = (st ) arr[0]; //<- Unboxing (casting and copy)

//YES
//Decalre the data type as class.
Class st
{
public int i;
}
Prefer String.Equal method instead of == operator.
Using the string.Equals method is much faster when the strings are matched. So
if you are very keen and need special type of performance and expect that most
of the strings will be the same, use the Equal method.
Note: The differences in performance are negligible and effect only numerous
operations.

Code snippets:

string s1= "code";


string s2 = "code1";
//No:
if(s1 == s2)
{
...
}

//Yes:
if(s1.Equals(s2))
{
...
}

Use Native Image Generator (nGen.exe) in case of long and heavy


initialization.

Pradeep Kumar Mishra Page 26


C# Coding Guidelines and Best Practices

The .NET Framework runs C# assemblies using the JIT compiler. Each code that
is executed for the first time is being compiled. In case of heavy and long
initialization in your assembly, you might want to use the nGen .NET tool.

"The nGen creates a native image from a managed assembly and installs it into
the native image cache on the local computer.

Once you create a native image for an assembly, the runtime automatically uses
that native image each time it runs the assembly. You do not have to perform
any additional procedures to cause the runtime to use a native image. Running
Ngen.exe on an assembly allows the assembly to load and execute faster,
because it restores code and data structures from the native image cache rather
than generating them dynamically." (MSDN 2005)

You do not have to lose the advantage of JIT compilation, because you can call
the nGen command on the installation machine through your project setup,
using:

ngen [options] [assemblyName |assemblyPath ]

Prefer 'for' over 'foreach'.

'foreach' and 'for' statements serve the same goal - run in loop over block of
statements.

The main differences in using the foreach statement are that you do not need to
deal with increments and with the end of the loop expression. Moreover, the
foreach statement is designed to traverse through the entire collection. One can
say that foreach is a private case of for.

In the code snippets below, we can see that both loop blocks produce the same
results, only under the hood the foreach hurts the performance. More variables
are involved and additional heavy array copy.

The foreach is far handier to use especially for collections but if your code runs
over large collections, prefer using 'for'.

Code snippets:

//foreach
int[] arrayOfInts= new int[5];
int sum= 0;
foreach(int i arrayOfInts) {
sum+= i;
}

//for
int[] arrayOfInts= new int[1];
int sum= 0;
for(int i = 0; i < arrayOfInts.Length; i++) {
sum+= arrayOfInts[i];

Pradeep Kumar Mishra Page 27


C# Coding Guidelines and Best Practices

Do not cast unnecessarily.


Duplicate casts degrade performance, especially when the casts are
performed in compact iteration statements. For explicit duplicate cast
operations, store the result of the cast in a local variable and use the local
variable instead of the duplicate cast operations.
If the C# is operator is used to test whether the cast will succeed before the
actual cast is performed, consider testing the result of the as operator
instead. This provides the same functionality without the implicit cast
operation performed by the is operator.
Example:
namespace PerformanceLibrary
{
public sealed class SomeClass
{
private SomeClass() {}

// NO.
public static void UnderPerforming(ArrayList list)
{
foreach(object obj in list)
{
// The 'is' statement performs a cast operation.
if(obj is Control)
{
// The 'as' statement performs a duplicate cast
operation.
Control aControl = obj as Control;
// Use aControl.
}

}
}

// Yes.
public static void BetterPerforming(ArrayList list)
{
foreach(object obj in list)
{
Control aControl = obj as Control;
if(aControl != null)
{
// Use aControl.
}
}
}
}
}

4.2 Usage
Use string.Empty instead of

Pradeep Kumar Mishra Page 28


C# Coding Guidelines and Best Practices

//Avoid
string name = ;
//Correct
string name = string.Empty;

Use the 'checked' keyword to avoid overflow.

Code snippets:

//NO
short shortNum;
int i = 32768;
shortNum = (short) i;
// problem after statment excution
// the shortNum variable has an uninitialized value,

//YES
try {
shortNum = checked((short)i); // solution
}
catch(OverflowException efx) {}

Use Explicit interface to 'hide' the implementation of an interface


Implementing an interface explicitly 'hides' the interface methods. Visual Studio
does not display the interface methods in the intellisense.

Code snippets:

//interface definition
public interface IChild{
bool IsHuman();
void lie();
}

//class definition
public Pinocchio: IChild {
IChild.IsHuman() //explicit interface implementation
{
}
public void Lie(); //regular interface implementation
}

//using the object


static void main()
{
// Visual studio will not display
// the isHuman mwthod in the intellisence.
Pinocchio o = new Pinocchio();
((IChild) o).IsHuman(); // using the IsHuman method explicitly.
o.Lie();
}
Collection properties should be read only.

Pradeep Kumar Mishra Page 29


C# Coding Guidelines and Best Practices

A writable collection property allows a user to replace the collection with an


entirely different collection. A read-only property stops the collection from
being replaced while still allowing the individual members to be set. If
replacing the collection is a goal, the preferred design pattern is to include a
method to remove all the elements from the collection and a method to re-
populate the collection.
Example: The following example shows a type with a writable collection
property and shows how the collection can be replaced directly. In addition,
the preferred manner of replacing a read-only collection property using Clear
and AddRange methods is shown.

namespace UsageLibrary
{
public class WritableCollection
{
ArrayList strings;

public ArrayList SomeStrings


{
get { return strings; }

// Violates the rule.


set { strings = value; }
}

public WritableCollection()
{
strings = new ArrayList(
new string[] {"IEnumerable", "ICollection", "IList"}
);
}
}

class ReplaceWritableCollection
{
static void Main()
{
ArrayList newCollection = new ArrayList(
new string[] {"a", "new", "collection"} );

// strings is directly replaced with newCollection.


WritableCollection collection = new
WritableCollection();
collection.SomeStrings = newCollection;

// newCollection is added to the cleared strings


collection.
collection.SomeStrings.Clear();
collection.SomeStrings.AddRange(newCollection);
}
}
}

Pradeep Kumar Mishra Page 30


C# Coding Guidelines and Best Practices

Use @ to ease the work with literal paths.

Code snippets:

//Old way
string sFilePath = c:\\a\\b\\c.txt;

//The C# way
string sFilePath = @c:\a\b\c.txt;
Make your API assembly CLS Compliant.
The CLS-Compliant attribute cause the compiler to check whether your public
exposed types in the assembly are CLS-Compliant.
Prefer to define the attribute for the entire assembly, especially for API. The
incentive to create a CLS compliant assembly is that any assembly written in one
of the .NET aware languages can use your assembly more efficiently because
there is no data type interoperability.

Code snippets:

using System;
[assembly:CLSCompliant(true)]

Define destructor and implement IDisposable interface for classes


that use native resource directly.
You should define a destructor whenever you use native code in your assembly,
i.e., use types that are not managed by the garbage collector. The compiler
changes the destructor method to Finalize method (can be seen in the MSIL using
the ILDasm.exe).
The Garbage collector marks a class with destructor as Finalized and calls the
Finalize method while destructing the object. This behavior guarantees that your
release of resources in the destructor will occur.
But, what if you want to release the resources immediately? For this purpose,
you should implement the IDisposable interface and call the Dispose method
when you want to release the object.
Note 1: Do not use destructor if your class does not use native / unmanaged
resources. If you do so, you create unnecessary work for the garbage collector.
Note 2: If you implement the IDisposable and a destructor, you should call the
Dispose method from the destructor to force the object to release resources
immediately.

Code snippets:

~my class {
if ( true == b)
{
}
}

//The MSIL code:


protected override void Finalize()
{
try {

Pradeep Kumar Mishra Page 31


C# Coding Guidelines and Best Practices

if ( true == b)
{
}
}
finally { base.Finalize();}
}
Avoid the use of GC.Collect.
The GC.Collect method forces garbage collection of all generations.
The performance is hurt during the call to GC.Collect, the application execution is
paused. Moreover, the method might cause the promotion of short lived objects
to a higher generation, i.e., those object live longer than it should in the first
place.
If you must use it in order to reclaim the maximum amount of available memory,
use the method only on generation 0.

Code snippets:

//No:
GO.Collect();
//Yes:
GC.Collect(0);

4.3 Design
Abstract type should not have public constructors
Constructors on abstract types can only be called by derived types. Because
public constructors create instances of a type, and you cannot create instances of
an abstract type, an abstract type with a public constructor is incorrectly
designed.
Example:
namespace DesignLibrary
{
public abstract class BadAbstractClassWithConstructor
{
//WRONG: AbstractTypesShouldNotHaveConstructors.
public BadAbstractClassWithConstructor()
{
// Add constructor logic here.
}
}

public abstract class GoodAbstractClassWithConstructor


{
protected GoodAbstractClassWithConstructor()
{
// Add constructor logic here.
}
}
}
Consider passing base types as parameters.
When a base type is specified as a parameter in a method declaration, any type
derived from the base type can be passed as the corresponding argument to the

Pradeep Kumar Mishra Page 32


C# Coding Guidelines and Best Practices

method. When the argument is used inside the method body, the specific method
that is executed depends on the type of the argument. If the additional
functionality provided by the derived type is not required, use of the base type
allows the method to be more widely utilized.
Example:
namespace DesignLibrary
{
public class StreamUser
{
int anInteger;

public void ManipulateFileStream(FileStream stream)


{
while((anInteger = stream.ReadByte()) != -1)
{
// Do something.
}
}

public void ManipulateAnyStream(Stream anyStream)


{
while((anInteger = anyStream.ReadByte()) != -1)
{
// Do something.
}
}
}

class TestStreams
{
static void Main()
{
StreamUser someStreamUser = new StreamUser();
MemoryStream testMemoryStream = new MemoryStream(new byte[]
{});
using(FileStream testFileStream =
new FileStream("test.dat", FileMode.OpenOrCreate))
{
// Cannot be used with testMemoryStream.
someStreamUser.ManipulateFileStream(testFileStream);

someStreamUser.ManipulateAnyStream(testFileStream);
someStreamUser.ManipulateAnyStream(testMemoryStream);
}
}
}
}

Enums should have zero value


The default value of an un-initialized enumeration, like other value types, is zero.
A non-flags attributed enumeration should define a member with the value of
zero so that the default value is a valid value of the enumeration. If appropriate,
name the member 'None'. Otherwise, assign zero to the most commonly used
member. Note that if the value of the first enumeration member is not set in the
declaration, its value is zero by default.

Pradeep Kumar Mishra Page 33


C# Coding Guidelines and Best Practices

If an enumeration that has the FlagsAttribute applied defines a zero-valued


member, its name should be 'None' to indicate that no values have been set in
the enumeration. Using a zero-valued member for any other purpose is contrary
to the use of the FlagsAttribute in that the AND and OR bitwise operators are
useless with the member. This implies that only one member should be assigned
the value zero. Note that if there are multiple members with the value zero in a
flags-attributed enumeration, Enum.ToString() returns incorrect results for
members that are not zero.
Example:
public enum TraceLevel
{
Off = 0,
Error = 1,
Warning = 2,
Info = 3,
Verbose = 4
}
//Correct
[Flags]
public enum TraceOptions
{
None = 0,
CallStack = 0x01,
LogicalStack = 0x02,
DateTime = 0x04,
Timestamp = 0x08,
}
//Wrong
[Flags]
public enum BadTraceOptions
{
CallStack = 0,
LogicalStack = 0x01,
DateTime = 0x02,
Timestamp = 0x04,
}

Mark assemblies with ComVisible.

Pradeep Kumar Mishra Page 34


C# Coding Guidelines and Best Practices

The ComVisibleAttribute attribute determines how COM clients access


managed code. Good design dictates that assemblies explicitly indicate COM
visibility. COM visibility can be set for an entire assembly and then overridden
for individual types and type members. If the attribute is not present, the
contents of the assembly are visible to COM clients.
[assembly: ComVisible(false)] //can be true or false

Properties should not be write only.


Get accessors provide read access to a property and set accessors provide
write access. While it is acceptable and often necessary to have a read-only
property, the design guidelines prohibit the use of write-only properties
because allowing a user to set a value, and then preventing the user from
viewing the value does not provide any security. Also, without read access,
the state of shared objects cannot be viewed, which limits their usefulness.
Example:
public class BadClassWithWriteOnlyProperty
{
string someName;

// Violates rule PropertiesShouldNotBeWriteOnly.


public string Name
{
set
{
someName = value;
}
}
}

public class GoodClassWithReadWriteProperty


{
string someName;

public string Name


{
get
{
return someName;
}
set
{
someName = value;
}
}
}

Do not use string as type for URI parameters/properties or URI


return values.
A string representation of a URI is prone to parsing and encoding errors,
and can lead to security vulnerabilities. If a method takes a string
representation of a URI, a corresponding overload should be provided that

Pradeep Kumar Mishra Page 35


C# Coding Guidelines and Best Practices

takes an instance of the Uri class, which provides these services in a safe
and secure manner.
Example:
namespace DesignLibrary
{
public class ErrorProne
{
string someUri;

// Violates rule UriPropertiesShouldNotBeStrings.


public string SomeUri
{
get { return someUri; }
set { someUri = value; }
}

// Violates rule UriParametersShouldNotBeStrings.


public void AddToHistory(string uriString) { }

// Violates rule UriReturnValuesShouldNotBeStrings.


public string GetRefererUri(string httpHeader)
{
return "http://www.adventure-works.com";
}
}

public class SaferWay


{
Uri someUri;

// To retrieve a string, call SomeUri.ToString().


// To set using a string, call SomeUri = new
Uri(string).
public Uri SomeUri
{
get { return someUri; }
set { someUri = value; }

Pradeep Kumar Mishra Page 36


C# Coding Guidelines and Best Practices

public void AddToHistory(string uriString)


{
// Check for UriFormatException.
AddToHistory(new Uri(uriString));
}

public void AddToHistory(Uri uriType) { }

public Uri GetRefererUri(string httpHeader)


{
return new Uri("http://www.adventure-works.com");
}
}
}

4.4 Globalization
Do not hardcode locale specific strings.
Example:
namespace GlobalizationLibrary
{
class WriteSpecialFolders
{
static void Main()
{
string string0 = "C:";

// Do Not.
string string1 = @"\Documents and Settings";
string string2 = @"\All Users";
string string3 = @"\Application Data";
Console.WriteLine(string0 + string1 + string2 +
string3);

// Do use
Console.WriteLine(Environment.GetFolderPath(

Environment.SpecialFolder.CommonApplicationData));
}
}
}

Do pass IFormat provider and CultureInfo.

Pradeep Kumar Mishra Page 37


C# Coding Guidelines and Best Practices

When a CultureInfo or System.IFormatProvider object is not supplied,


the default value supplied by the overloaded member might not have the
effect that you want in all locales. Also, .NET Framework members choose
default culture and formatting based on assumptions that might not be
correct for your code. To ensure the code works as expected for your
scenarios, you should supply culture-specific information
Example:

namespace GlobalLibGlobalLibrary
{
public class IFormatProviderTest
{
public static void Main()
{
string dt = "6/4/1900 12:15:12";

// The default behavior of DateTime.Parse is to use


// the current culture.

// Here IFormatProvider has not been specified.


DateTime myDateTime = DateTime.Parse(dt);
Console.WriteLine(myDateTime);
//This gives out put as 6/4/1900 12:15:12 PM

// Change the current culture to the required culture (e.g


French),
// and parsing the same string yields a different value.

Thread.CurrentThread.CurrentCulture = new
CultureInfo("Fr-fr", true);
myDateTime = DateTime.Parse(dt);

Console.WriteLine(myDateTime);
//This gives out put as 06/04/1900 12:15:12

}
}
}

4.5 Security

Array fields should not be read only


When you apply the readonly modifier to a field that contains an array, the
field cannot be changed to refer to a different array; however, the elements of
the array stored in a read-only field can be changed. Code that makes
decisions or performs operations based on the elements of a publicly accessible
read-only array might contain an exploitable security vulnerability.
Example:
public class MyClassWithReadOnlyArrayField
{

Pradeep Kumar Mishra Page 38


C# Coding Guidelines and Best Practices

// The field grades is public, and therefore vulnerable to any


caller.
public readonly int[] grades = {90, 90, 90};

// The field privateGrades is private, but is still vulnerable


because it is returned to callers by the GetPrivateGrades method.

private readonly int[] privateGrades = {90, 90, 90};


private readonly int[] securePrivateGrades = {90, 90, 90};

// Making the array private does not protect it because it is


passed to others.
public int[] GetPrivateGrades()
{
return privateGrades;
}
}

Asserts require security demands


Asserting security permission without performing any security checks can
leave an exploitable security weakness in your code
SecurityPermission securePermission
= new SecurityPermission(PermissionState.Unrestricted);
/*Demand some permission(s) that all callers must have before
you will assert permission on their behalf.
In this example, all callers are required to have all security
permissions before permission to access environment variables is
asserted.
*/
securePermission.Demand();
//If the demand fails, a SecurityException is thrown.

Public types that implement non-public interfaces should be sealed


Interface methods have public accessibility, which cannot be changed by the
implementing type. An internal interface creates a contract that is not intended
to be implemented outside the assembly that defines the interface. A public
type that implements a method of an internal interface using the virtual
modifier allows the method to be overridden by a derived type that is outside
the assembly. If a second type in the defining assembly calls the method and
expects an internal-only contract, behavior might be compromised when,
instead, the overridden method in the outside assembly is executed. This
creates security vulnerability.
// Internal by default.
interface IValidate
{
bool UserIsValidated();
}

Pradeep Kumar Mishra Page 39


C# Coding Guidelines and Best Practices

public class BaseImplementation : IValidate


{
public virtual bool UserIsValidated()
{
return false;
}
}

public class UseBaseImplementation


{
public void SecurityDecision(BaseImplementation
someImplementation)
{
if(someImplementation.UserIsValidated() == true)
{
Console.WriteLine("Account number & balance.");
}
else
{
Console.WriteLine("Please login.");
}
}
}

Virtual methods and their Overrides require the same Link Demand
status.
If a virtual method has a Link Demand, so should any override of it, and if an
Override has a Link Demand, so should the overridden virtual method
Ex: If a method in the base class is declared as virtual, and has the security
status, then immediate overriding of that member should also have the same
security status.
Example:
public interface ITestOverrides
{
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand,
Unrestricted=true)]
Object GetFormat(Type formatType);
}

public class OverridesAndSecurity : ITestOverrides


{
// Rule violation: The interface has security, and this
implementation does not.
object ITestOverrides.GetFormat(Type formatType)
{
return (formatType == typeof(OverridesAndSecurity) ?
this : null);
}

// These two methods are overridden by DerivedClass and


DoublyDerivedClass.
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand,
Unrestricted=true)]
public virtual void DoSomething()

Pradeep Kumar Mishra Page 40


C# Coding Guidelines and Best Practices

{
Console.WriteLine("Doing something.");
}

public virtual void DoSomethingElse()


{
Console.WriteLine("Doing some other thing.");
}
}

public class DerivedClass : OverridesAndSecurity,


ITestOverrides
{
// Rule violation: The interface has security, and this
implementation does not.
public object GetFormat(Type formatType)
{
return (formatType == typeof(OverridesAndSecurity) ?
this : null);
}

// Rule violation: This does not have security, but the


base class version does.
public override void DoSomething()
{
Console.WriteLine("Doing some derived thing.");
}

// Rule violation: This has security, but the base class


version does not.
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand,
Unrestricted=true)]
public override void DoSomethingElse()
{
Console.WriteLine("Doing some other derived thing.");
}
}

public class DoublyDerivedClass : DerivedClass


{
// The OverridesAndSecurity version of this method does not
have security.
// Base class DerivedClass's version does.
// The DoublyDerivedClass version does not violate the
rule, but the
// DerivedClass version does violate the rule.
public override void DoSomethingElse()
{
Console.WriteLine("Doing some other derived thing.");
}
}

Static constructors should be private


A static constructor, also called a class constructor, is used to initialize a type.
The system calls the static constructor before the first instance of the type is

Pradeep Kumar Mishra Page 41


C# Coding Guidelines and Best Practices

created or any static members are referenced. The user has no control over
when the static constructor is called. If a static constructor is not private, it
can be called by code other than the system. Depending on the operations
performed in the constructor, this can cause unexpected behavior.

4.6 Interoperability
Do not use AutoDual ClassInterfaceType
Types that use a dual interface allow clients to bind to a specific interface
layout. Any changes in a future version to the layout of the type or any base
types will break COM clients that bind to the interface. By default, if the
ClassInterfaceAttribute attribute is not specified, a dispatch-only interface
is used.
Example:
// This violates the rule.
[ClassInterface(ClassInterfaceType.AutoDual)]
public class DualInterface
{
public void SomeMethod() {}
}

public interface IExplicitInterface


{
void SomeMethod();
}

[ClassInterface(ClassInterfaceType.None)]
public class ExplicitInterface : IExplicitInterface
{
public void SomeMethod() {}
}
Use StructLayout attribute for classes and structs when using COM
Interop.
The attributes cause the compiler to pack the structure in sequential memory
so that it can be sent to unmanaged code correctly (unmanaged code that
expects a specific layout).

Code snippets:

using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct st
{
int i;
float f;
}
Auto layout types should not be ComVisible
Auto layout types are managed by the common language runtime. The layout
of these types can change between versions of the .NET Framework, which
will break COM clients that expect a specific layout. Note that if the

Pradeep Kumar Mishra Page 42


C# Coding Guidelines and Best Practices

StructLayoutAttribute attribute is not specified, the C#, Visual Basic, and


C++ compilers specify the Sequential layout for value types
Example:
// This violates the rule.
[StructLayout(LayoutKind.Auto)]
[ComVisible(true)]
public struct AutoLayout
{
public int ValueOne;
public int ValueTwo;
}

// This satisfies the rule.


[StructLayout(LayoutKind.Explicit)]
[ComVisible(true)]
public struct ExplicitLayout
{
[FieldOffset(0)]
public int ValueOne;

[FieldOffset(4)]
public int ValueTwo;
}

Avoid overloads in ComVisible interfaces


When overloaded methods are exposed to COM clients, only the first method
overload retains its name. Subsequent overloads are uniquely renamed by
appending to the name an underscore character '_' and an integer that
corresponds to the order of declaration of the overload. For example, consider
the following methods:
void SomeMethod(int valueOne);
void SomeMethod(int valueOne, int valueTwo, int valueThree);
void SomeMethod(int valueOne, int valueTwo);

These methods are exposed to COM clients as:

void SomeMethod(int valueOne);


void SomeMethod_2(int valueOne, int valueTwo, int valueThree);
void SomeMethod_3(int valueOne, int valueTwo);

Example:
// This interface violates the rule.
[ComVisible(true)]
public interface IOverloadedInterface
{
void SomeMethod(int valueOne);
void SomeMethod(int valueOne, int valueTwo);
}

// This interface satisfies the rule.


[ComVisible(true)]
public interface INotOverloadedInterface
{
void SomeMethod(int valueOne);

Pradeep Kumar Mishra Page 43


C# Coding Guidelines and Best Practices

void AnotherMethod(int valueOne, int valueTwo);


}

4.7 Exceptions
A well-designed set of error handling code blocks can make a program more robust
and less prone to crashing because the application handles such errors. The following
list contains suggestions on best practices for handling exceptions:
Know when to set up a try/catch block. For example, you can
programmatically check for a condition that is likely to occur without using
exception handling. In other situations, using exception handling to catch an
error condition is appropriate.
The method you choose depends on how often you expect the event to occur.
If the event is truly exceptional and is an error (such as an unexpected end-
of-file), using exception handling is better because less code is executed in
the normal case. If the event happens routinely, using the programmatic
method to check for errors is better. In this case, if an exception occurs, the
exception will take longer to handle.
Use try/finally blocks around code that can potentially generate an exception
and centralize your catch statements in one location. In this way, the try
statement generates the exception, the finally statement closes or deallocates
resources, and the catch statement handles the exception from a central
location.
Always order exceptions in catch blocks from the most specific to the least
specific. This technique handles the specific exception before it is passed to a
more general catch block.
End exception class names with the word "Exception.".
When creating user-defined exceptions, you must ensure that the metadata
for the exceptions is available to code executing remotely, including when
exceptions occur across application domains. For example, suppose
Application Domain A creates Application Domain B, which executes code that
throws an exception. For Application Domain A to properly catch and handle
the exception, it must be able to find the assembly containing the exception
thrown by Application Domain B. If Application Domain B throws an exception
that is contained in an assembly under its application base, but not under
Application Domain A's application base, Application Domain A will not be able
to find the exception and the common language runtime will throw a
FileNotFoundException. To avoid this situation, you can deploy the assembly
containing the exception information in two ways:
o Put the assembly into a common application base shared by both
application domains, or
o If the domains do not share a common application base, sign the
assembly containing the exception information with a strong name and
deploy the assembly into the global assembly cache.
In C#, use at least the three common constructors when creating your own
exception classes.

Pradeep Kumar Mishra Page 44


C# Coding Guidelines and Best Practices

using System;
public class ListNotFoundException: ApplicationException
{
public ListNotFoundException()
{
}
public ListNotFoundException(string message) : base(message)
{
}
public ListNotFoundException(string message, Exception inner)
: base(message, inner)
{
}
}
In most cases, use the predefined exceptions types. Define new exception
types only for programmatic scenarios. Introduce a new exception class to
enable a programmer to take a different action in code based on the
exception class.
Do not derive user-defined exceptions from the Exception base class. For
most applications, derive custom exceptions from the Application Exception
class.
Instead of sending error messages directly to the user, in case error occurs ,
the exception code should be returned and then based on users preferred
language appropriate messages should be shown to the user.
Use grammatically correct error messages, including ending punctuation.
Each sentence in a description string of an exception should end in a period.
Provide Exception properties for programmatic access. Include extra
information in an exception (in addition to the description string) only when
there is a programmatic scenario where the additional information is useful.
Return null for extremely common error cases. For example, File.Open
returns null if the file is not found, but throws an exception if the file is
locked.
Design classes so that an exception is never thrown in normal use. For
example, a FileStream class exposes another way of determining whether the
end of the file has been reached. This avoids the exception that is thrown if
you read past the end of the file.
Throw an InvalidOperationException if a property set or method call is not
appropriate given the object's current state.
Throw an ArgumentException or a class derived from ArgumentException if
bad parameters are passed.

Pradeep Kumar Mishra Page 45


C# Coding Guidelines and Best Practices

The stack trace begins at the statement where the exception is thrown and
ends at the catch statement that catches the exception. Be aware of this fact
when deciding where to place a throw statement.
Use exception builder methods. It is common for a class to throw the same
exception from different places in its implementation. To avoid excessive
code, use helper methods that create the exception and return it.
Alternatively, use the exception's constructor to build the exception. This is
more appropriate for global exception classes, such as ArgumentException.
Throw exceptions instead of returning an error code or HRESULT.
Clean up intermediate results when throwing an exception. Callers should be
able assume that there are no side effects when an exception is thrown from
a method.
Additional information about throwing exceptions:
There is a basic difference between two kinds of exception thrown. So here it
should not only be 'where' but 'what' also.
For more information please see
http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/cpguide/html/cpconbestpracticesforhandlingexceptions.asp
The statement
throw e;
This will reset the stack trace in exception object "e" so that the stack trace
begins on the same line as the throw statement. The statement:
throw;
This will re-throw the original exception object without resetting the stack
trace. So if you catch an exception, examine and decide you don't really want to
handle it and have nothing of value to add, then simply use
throw;
If you want to erase from the stack trace the original line that generated the
exception (in essence, hide information) then use:
throw e;

If you want to add context (this is usually most people do), then use something
like this:
throw new Exception("Message",ex);
This preserves the original exception as the inner exception in the exception
object.

5 Project Settings and Structure


Always build your project with warning level 4.

Pradeep Kumar Mishra Page 46


C# Coding Guidelines and Best Practices

Always explicitly state your supported runtime version in the application


configuration file
<? xml version="1.0" ?>
<configuration>
<startup>
<supportedRuntime version="1.1.5000.0" />
<supportedRuntime version="2.0.5500.0" />
</startup>
</configuration>
Populate all fields in AssemblyInfo.cs such as company name, description and
copyright notice. For more information please see any automatically created
Assemblyinfo.cs file. Following values must be used for the attribute given
below

[assembly: AssemblyCompany("Company Inc.")]


[assembly: AssemblyCopyright("Copyright (c) "+ Year + " Company
, Inc.")]
[assembly: AssemblyTrademark("Company")]
[assembly: AssemblyCulture("")]
[assembly:CLSCompliant(true)]

Pradeep Kumar Mishra Page 47


C# Coding Guidelines and Best Practices

Put copyright notice also on the top of every cs file. Following copyright
information should be used.
#region Copyright (c) 2005-2006 Company , Inc. All rights
reserved
// **************************************************************
// Copyright (c) 2005 Company.
//
//The copyright to the computer software herein is proprietary
//and remains the property of Company. It may //be used and/or
copied only with the written consent of Company // or in
accordance with the terms and conditions //stipulated in the
agreement/contract under which this software //has been supplied.
// **************************************************************
#endregion

Note: You can modify templates in the Visual studio so that each time you add a
new file the copyright notice is automatically added to the created file.
E.g. To add copyright notice when a new class is created change the following
template(For VS .NET 2003)
<VS Installation Directory>\Microsoft Visual Studio .NET
2003\VC#\VC#Wizards\CSharpAddClassWiz\Templates\1033\ NewCSharpFile.cs
All assembly references in the same solution should use relative path. (This is
by default in Visual Studio)
Always sign your assemblies, including client applications. For more
information regarding signing assemblies please see any automatically
created Assemblyinfo.cs file.
Make sure there are no cyclic references among assemblies.

6 References:
http://msdn2.microsoft.com/en-us/library/ms229042(en-US,VS.80).aspx
http://www.gotdotnet.com/Team/FxCop/docs/rules/NamingRules.aspx
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=336
http://www.tech.purdue.edu/Cpt/Courses/cpt355/C_Sharp_Coding_Standards
_and_Guidelines.htm
http://www.icsharpcode.net/TechNotes/SharpDevelopCodingStyle03.pdf

Pradeep Kumar Mishra Page 48


C# Coding Guidelines and Best Practices

Appnedix A
Hungarian notation: It describes the practice of prefixing variable names with a
tag that indicates what kind of object or data they hold. Contrary to popular belief,
this does not mean that the prefix should indicate the data type of the variable, but,
rather, it should give someone reviewing the code a clue as to what the variable
contains and what it is intended to be used for. Prefixing a variable with a tag
indicating its data type is better known as System notation, and should not be used.
Example of Hungarian notation for variables:
int rowCount = 0;
int colCount = 0;
Notice that both variables have integer data types, and both are intended to hold a
count of something, but it is very clear that rowCount is a count of a rows, and
colCount is a count of columns.
Anyone reviewing the following code:
rowCount = userData.Columns.Count;
colCount = userData.Rows.Count;
would be able to spot an error in the usage of the variables.
Hungarian notation, when used properly, gives a readable quality to the code, and
makes code easier to write, debug, and maintain because it helps correct code look
correct, and incorrect code look incorrect.
Hungarian notation is not recommended in c# by Microsoft. Some of the reasons
why not to use Hungarian notations are as follows
In object-oriented programming, it is rarely necessary as code is modularized
enough so that the declaration is often visible or easily found if there is a
question as to an identifiers type.
The VS.NET IDE offers several ways to quickly identify an objects type:
o Tooltip; seen by mousing over the identifier.
o Intellisense tells you the type.
o Type information is provided in the debugger.
You can often tell an identifiers type by the usage. This is particularly true in
OO environments where you often set an identifier to a new followed by the
type or when a method tells you what type it returns, such as
GetXmlReader() or LoadControl().

Pradeep Kumar Mishra Page 49

Vous aimerez peut-être aussi