Majority of the programmers write 'working code', but not ‘good code'.
Writing 'good code' is an art and you must learn and practice it.
Everyone may have different definitions for the term ‘good code’. In my
definition, the following are the characteristics of good code.
·
Reliable
·
Maintainable
·
Efficient
Most of the time we inclined towards writing code for higher
performance, compromising reliability and maintainability, But considering the
long term ROI (Return On Investment), efficiency and performance comes below
reliability and maintainability. If the code is not reliable and maintainable,
lots of time will be spent on identifying the issue, trying to understand code etc.
throughout the life of your application.
Purpose
The purpose of this document
is to provide coding style standards for the development of source code written
in C#. Adhering to a coding style standard is an industry proven best-practice
for making team development more efficient and application maintenance more
cost-effective. While not comprehensive, these guidelines represent the minimum
level of standardization expected in the source code of projects written in C#.
Specifically, this document covers Naming
Conventions, Coding Style, Language Usage, and Object Model Design.
Scope
This document provides guidance on the formatting,
commenting, naming, and programming style of C# source code and is applicable
to component libraries. web services, web sites, and rich client applications.
This document only applies to the C#
Language and the .NET Framework Common Type System (CTS) it implements.
Although the C# language is implemented alongside the .NET Framework, this
document does not address usage of.NET Framework class libraries. However,
common patterns and problems related to C#’s usage of the .NET Framework are
addressed in a limited fashion.
Document Conventions
Example code is shown using the Code font
and shows syntax as it would be color coded in Visual Studio’s code editor.
Purpose of coding standards and best practices
To develop reliable and maintainable
applications, you must follow coding standards and best practices.
The naming conventions, coding standards and
best practices described in this document are compiled from our own experience
and by referring to various Microsoft and non-Microsoft guidelines.
There are several standards exists in the
programming industry. None of them are wrong or bad and may follow any of them.
What is more important is, selecting one standard approach and ensuring that
everyone is following it.
How to follow the standards across the team
If the team
is with different skills and tastes, it is going to have a tough time convincing
everyone to follow the same standards. The best approach is to have a team
meeting and developing own standards document. This document is used as a
template.
Distribute a
copy of this document well ahead of the coding standards meeting. All members
should come to the meeting prepared to discuss pros and cons of the various
points in the document. Make sure you have a manager present in the meeting to
resolve conflicts.
Discuss all
points in the document. Everyone may have a different opinion about each point,
but at the end of the discussion, all members must agree upon the standard you
are going to follow. Prepare a new standards document with appropriate changes
based on the suggestions from all of the team members. Print copies of it and
post it in all workstations.
After
you start the development, there must schedule code review meetings to ensure
that everyone is following the rules. 3 types of code reviews are recommended:
- Peer review – another team member review the code to ensure that the code follows the coding standards and meets requirements. This level of review can include some unit testing also. Every file in the project must go through this process.
- Architect review – the architect of the team must review the core modules of the project to ensure that they adhere to the design and there is no “big” mistakes that can affect the project in the long run.
- Group review – randomly select one or more files and conduct a group review once in a week. Distribute a printed copy of the files to all team members 30 minutes before the meeting. Let them read and come up with points for discussion. In the group review meeting, use a projector to display the file content in the screen. Go through every sections of the code and let every member give their suggestions on how could that piece of code can be written in a better way.
Document Conventions
Coloring & Emphasis:
Blue Text
colored blue indicates a C# keyword or .NET type.
Bold Text
with additional emphasis to make it stand-out.
Keywords:
Always Emphasizes
this rule must be enforced.
Never Emphasizes
this action must not happen.
Do Not Emphasizes this action must not happen.
Avoid Emphasizes
that the action should be prevented, but some exceptions may exist.
Try Emphasizes
that the rule should be attempted whenever possible and appropriate.
Example Precedes
text used to illustrate a rule or recommendation.
Reason Explains
the thoughts and purpose behind a rule or recommendation.
Terminology and Definitions
The
following terminology is referenced throughout this document:
Access
Modifier
C#
keywords public, protected,
internal,
and private declare the allowed
code-accessibility of types and their members. Although default access
modifiers vary, classes and most other members use the default of private.
Notable exceptions are interfaces and
enums which both default to public.
Camel
Case
A
word with the first letter lowercase, and the first letter of each subsequent
word-part capitalized.
Example:
customerName
Common
Type System
The
.NET Framework common type system (CTS) defines how types are declared, used,
and managed. All native C# types are based upon the CTS to ensure support for
cross-language integration.
Identifier
A
developer defined token used to uniquely name a declared object or object
instance.
Example:
public class MyClassNameIdentifier
{…}
Pascal
Case
A
word with the first letter capitalized, and the first letter of each subsequent
word-part capitalized.
Example:
CustomerName
Premature
Generalization
As it applies to object model design; this is
the act of creating abstractions within an object model not based upon concrete
requirements or a known future need for the abstraction. In simplest terms:
“Abstraction for the sake of Abstraction.”
Flags
The
following flag are used to help clarify or categorize certain statements:
[C#v2+]
A
flag to identify rules and statements that apply only to C# Language
Specification v2.0 or greater.
Quick Summary
This
section contains tables describing a high-level summary of the major standards
covered in this document.
Coding Style
Code
|
Style
|
Source
Files
|
One
Namespace per file and one class per file.
|
Curly
Braces
|
On
new line. Always use braces when
optional.
|
Indention
|
Use
tabs with size of 4.
|
Comments
|
Use
// or ///
but not /* … */ and do not flowerbox.
|
Variables
|
One
variable per declaration.
|
Language Usage
Code
|
Style
|
Native
Data Types
|
Use
built-in C# native data types vs .NET CTS types.
|
(Use
int NOT Int32)
|
|
Enums
|
Avoid
changing default type.
|
Generics
[C#v2+]
|
Prefer
Generic Types over standard or strong-typed classes.
|
Properties
|
Never
prefix with Get or Set.
|
Methods
|
Use
a maximum of 5 parameters.
|
base
and this
|
Use
only in constructors or within an override.
|
Ternary
conditions
|
Avoid
complex conditions.
|
foreach
statements
|
Do
not modify enumerated items within a foreach statement.
|
Conditionals
|
Avoid
evaluating Boolean conditions against true or false.
|
No
embedded assignment.
|
|
Avoid
embedded method invocation.
|
|
Exceptions
|
Do
not use exceptions for flow control.
|
Use
throw; not throw e;
when re-throwing.
|
|
Only
catch what you can handle.
|
|
Use
validation to avoid exceptions.
|
|
Derive
from Execption not ApplicationException.
Avoid
generic Exception , Catch specific exception and log the error accordingly
Use
finally block to clear the objects.
|
|
Events
|
Always
check for null before invoking.
|
Locking
|
Use
lock()
not Monitor.Enter().
|
Do
not lock on an object type or “this”.
|
|
Do
lock on private objects.
|
|
Dispose()
& Close()
|
Always
invoke them if offered, declare where needed.
|
Finalizers
|
Use
the C# Destructors. Do not create Finalize() method.
|
AssemblyVersion
|
Increment
manually.
|
ComVisibleAttribute
|
Set
to false for all assemblies.
|
Naming Convention
“c”
|
=
|
camelCase
|
|||||||
“P”
|
=
|
PascalCase
|
|||||||
“_”
|
=
|
Prefix with _Underscore
|
|||||||
“ x”
|
=
|
Not Applicable.
|
|||||||
Identifier
|
Public
|
Protected
|
Internal
|
Private
|
Notes
|
||||
Project
File
|
P
|
x
|
x
|
x
|
Match
Assembly & Namespace.
|
||||
Source
File
|
P
|
x
|
x
|
x
|
Match
contained class.
|
||||
Other
Files
|
P
|
x
|
x
|
x
|
Apply
where possible.
|
||||
Namespace
|
P
|
x
|
x
|
x
|
Partial
Project/Assembly match.
|
||||
Class or Struct
|
P
|
P
|
P
|
P
|
Add
suffix of subclass.
|
||||
Interface
|
P
|
P
|
P
|
P
|
Prefix
with a capital I.
|
||||
Generic
Class
[C#v2+]
|
P
|
P
|
P
|
P
|
Use
T or K
as Type identifier.
|
||||
Method
|
P
|
P
|
P
|
P
|
Use
a Verb or Verb-Object pair.
|
||||
Property
|
P
|
P
|
P
|
P
|
Do
not prefix with Get or Set.
|
||||
Field
|
P
|
P
|
P
|
c
|
Only
use Private fields.
|
||||
No Hungarian Notation!
|
|||||||||
Constant
|
P
|
P
|
P
|
_c
|
|||||
Static
Field
|
P
|
P
|
P
|
_c
|
Only
use Private fields.
|
||||
Enum
|
P
|
P
|
P
|
P
|
Options
are also PascalCase.
|
||||
Delegate
|
P
|
P
|
P
|
P
|
|||||
Event
|
P
|
P
|
P
|
P
|
|||||
Inline
Variable
|
x
|
x
|
x
|
c
|
Avoid
single-character and enumerated names
|
||||
Parameter
|
x
|
x
|
x
|
c
|
|||||
The following guidelines are
applicable to all aspects C# development:
o Follow the style of existing
code. Strive to maintain consistency within the code base of an application. If
further guidance is needed, look to these guidelines and the .NET framework for
clarification and examples.
o Make code as simple and
readable as possible. Assume that someone else will be reading your code.
o Prefer small cohesive
classes and methods to large monolithic ones.
o Use a separate file for each
class, struct, interface, enumeration, and delegate with the exception of those
nested within another class.
o Write the comments first.
When writing a new method, write the comments for each step the method will
perform before coding a single statement. These comments will become the
headings for each block of code that gets implemented.
o Use liberal, meaningful
comments within each class, method, and block of code to document the purpose of the code.
o Mark incomplete code with
// TODO:
comments. When working with many classes at once, it
can be very easy to lose a train of thought.
o Never hard code “magic”
values into code (strings or numbers). Instead, define constants, static
read-only variables, and enumerations or read the values from configuration or
resource files.
o Prefer while and foreach over other available looping
constructs when applicable. They are logically simpler and easier to code and
debug.
o Use the StringBuilder class and it’s Append(), AppendFormat(), and ToString() methods instead
of the string concatenation operator (+=) for much more efficient use of memory.
o Be
sure Dispose() gets
called on IDisposable
objects that you create locally within a method. This is most commonly done in
the finally
clause of a try block. It’s done automatically when a using statement is
used.
o Never present debug
information to yourself or the end user via the UI (e.g. MessageBox). Use tracing
and logging facilities to output debug information.
Naming Conventions & Rules
o Always
use Camel Case or Pascal Case names.
o Avoid
ALL CAPS and all lowercase names. Single lowercase words or letters are
acceptable.
o Do
not create declarations of the same type (namespace, class, method, property,
field, or parameter) and access modifier (protected, public,
private, internal) that vary only by capitalization.
o Do
not use names that begin with a numeric character.
o Do
add numeric suffixes to identifier names.
o Always
choose meaningful and specific names.
o Variables
and Properties should describe an entity not the type or size.
o Do
not use Hungarian Notation
Example: strName
or
iCount
o Avoid
using abbreviations unless the full name is excessive.
o Avoid
abbreviations longer than 5 characters.
o Any
Abbreviations must be widely known and accepted.
o Use
uppercase for two-letter abbreviations, and Pascal Case for longer
abbreviations.
o Do
not use C# reserved words as names.
o Avoid
naming conflicts with existing .NET Framework namespaces, or types.
o Avoid
adding redundant or meaningless prefixes and suffixes to identifiers
Example:
// Bad!public enum ColorsEnum {…} public class CVehicle {…}public struct RectangleStruct {…}
o Do
not include the parent class name within a property name.
Example: Customer.Name
NOT
Customer.CustomerName
o Try
to prefix Boolean variables and properties with “Can”,
“ Is” or “ Has”.
o Append
computational qualifiers to variable names like Average,
Count,
Sum,
Min,
and Max where appropriate.
aaaaa
o When
defining a root namespace, use a Product, Company, or Developer Name as the root.
Example: Namespace.StringUtilities
Name Usage & Syntax
Identifier
|
Naming
Convention
|
||
Project
File
|
Pascal
Case.
|
||
Always
match Assembly Name & Root Namespace.
|
|||
Example:
|
|||
Namespace.Web.csproj ->
Namespace.Web.dll -> namespace Namespace.Web
|
|||
Source
File
|
Pascal
Case.
|
||
Always
match Class name and file name.
|
|||
Avoid
including more than one Class, Enum
(global), or Delegate (global) per file. Use a descriptive file name when
containing multiple Class, Enum,
or Delegates.
|
|||
Example:
|
|||
MyClass.cs =>
|
public class MyClass
|
||
{…}
|
|||
Resource
|
Try to use Pascal Case.
|
||
or
|
|||
Embedded File
|
Use
a name describing the file contents.
|
||
Namespace
|
Pascal
Case.
|
||
Try
to partially match Project/Assembly Name.
|
|||
Example:
|
|||
namespace Namespace.Web
|
|||
{…}
|
|||
Class
or Struct
|
Pascal
Case.
|
||
Use
a noun or noun phrase for class name.
|
|||
Add
an appropriate class-suffix when sub-classing another type when possible.
|
|||
Examples:
|
|||
private class MyClass
|
|||
{…}
|
|||
internal class SpecializedAttribute : Attribute
|
|||
{…}
|
|||
public class CustomerCollection
: CollectionBase
|
|||
{…}
|
|||
public class CustomEventArgs : EventArgs
|
|||
{…}
|
|||
private struct ApplicationSettings
|
|||
{…}
|
|||
Interface
|
Pascal
Case.
|
||
Always
prefix interface name with capital “I”.
|
|||
Example:
|
|||
interface ICustomer
|
|||
{…}
|
|||
Generic
Class
|
Always
use a single capital letter, such as T or K.
|
||
&
|
Example:
|
||
public class FifoStack<T>
|
|||
Generic
|
{
|
||
Parameter Type
|
public void Push(<T> obj)
|
||
{…}
|
|||
[C#v2+]
|
public <T>
Pop()
|
||
{…}
|
|||
}
|
|||
Method
|
Pascal
Case.
|
||
Try to use a Verb or Verb-Object
pair.
|
|||
Example:
|
|||
public void Execute() {…}
|
|||
private string GetAssemblyVersion(Assembly target)
{…}
|
|||
Property
|
Pascal
Case.
|
||
Property
name should represent the entity it returns. Never prefix property names with
|
|||
“Get”
or “ Set”.
|
|||
Example:
|
|||
public string Name
|
|||
{
|
|||
get{…}
|
|||
set{…}
|
|||
}
|
|||
Field
|
Pascal
Case.
|
||
Avoid
using non-private Fields
|
|||
(Public,
Protected,
|
Use
Properties instead.
|
||
or
Internal)
|
Example:
|
||
public string Name;
|
|||
protected IList InnerList;
|
|||
Field
(Private)
|
Camel
Case and prefix with a single underscore (_)
character.
|
||
Example:
|
|||
private string _name;
|
|||
Constant
or
|
Treat
like a Field.
|
||
Static Field
|
Choose
appropriate Field access-modifier above.
|
||
Enum
|
Pascal
Case (both the Type and the Options).
|
||
Add
the FlagsAttribute to bit-mask multiple options.
|
|||
Example:
|
|||
public enum CustomerTypes
|
|||
{
|
|||
Consumer,
|
|||
Commercial
|
|||
}
|
|||
Delegate
or Event
|
Treat
as a Field.
|
||
Choose
appropriate Field access-modifier above.
|
|||
Example:
|
|||
public event EventHandler LoadPlugin;
|
|||
Variable
(inline)
|
Camel
Case.
|
||
Avoid
using single characters like “x” or “ y”
except in FOR loops.
|
|||
Avoid
enumerating variable names like text1, text2,
text3 etc.
|
|||
Parameter
|
Camel
Case.
|
||
Example:
|
|||
public void Execute(string
commandText, int iterations)
|
|||
{…}
|
|||
Guidelines:
o
In Pascal casing, the first letter of an
identifier is capitalized as well as the first letter of each concatenated
word. This style is used for all public
identifiers within a class library, including namespaces, classes and
structures, properties, and methods.
o
In Camel casing, the first letter of an
identifier is lowercase but the first letter of each concatenated word is
capitalized. This style is used for private and protected identifiers within
the class library, parameters passed to methods, and local variables within a
method.
o
Upper casing is used only for abbreviated
identifiers and acronyms of four letters or less.
o
The Programming section
of this document provides naming templates for each construct within the C#
language. These templates can be used in conjunction with the tables provided
in Appendix A. Naming Parts & Pairs to
yield meaningful names in most scenarios.
Formatting
2.1.1 Class Layout
Classes should be organized
into regions within an application using a layout determined by your
application architect. These may be based on accessibility, type, or
functionality. Consult the architect for the layout strategy used in your
application.
Example:
// Class layout
based on accessibility
class Purchasing
{
#region Main
#region Public
#region Internal
#region
Protected
#region Private
#region Extern
#region Designer
Generated Code
}
Guidelines:
o
Use the same layout consistently in all classes in an application.
o Omit regions if their associated
class elements are not needed.
o The Designer Generated Code region created by Visual Studio’s Visual Designer should never be
modified by hand. It should contain only code generated by the designer.
2.1.2 Indicating Scope
Indicate scope when
accessing all static and non-static class members. This provides a crystal
clear indication of the intended use of the member. VisualStudio.NET
intellisense is automatically invoked when using this practice, providing a
list of all available class members. This helps prevent unnecessary typing and reduces
the risk of typographic errors.
Example:
string
connectionString = DataAccess.DefaultConnectionString;
float amount
= this.CurrentAmount;
this.discountedAmount
= this.CalculateDiscountedAmount( amount, this.PurchaseMethod );
Guidelines:
o
Include the this keyword before all member fields, properties and
methods.
o
Include the name of the class before all static fields, constants,
fields, and methods.
2.1.3 Indentation & Braces
Statements should be
indented (using tabs) into blocks that show relative scope of execution. A consistent
tab size should be used for all indentation in an application. Braces, when
necessary, should be placed directly below and aligned with the statement that
begins a new scope of execution. Visual Studio.NET includes a keyboard short-cut
that will automatically apply this format to a selected block of code.
Example:
float CalculateDiscountedAmount( float amount, PurchaseMethod purchaseMethod )
{
// Calculate the discount based on the purchase method
float discount = 0.0f;
switch( purchaseMethod )
{
case PurchaseMethod.Cash:
// Calculate the cash discount
discount
= this.CalculateCashDiscount( amount );
Trace.Writeline(
“Cash discount of {0} applied.”, discount );
break;
case PurchaseMethod.CreditCard:
// Calculate the credit card discount
discount
= this.CalculateCreditCardDiscount( amount );
Trace.WriteLine(
“Credit card discount of {0} applied.”, discount
);
break;
default:
// No discount applied for other purchase methods
Trace.WriteLine(
“No discount applied.” );
break;
}
// Compute the discounted
amount, making sure not to give money away
float discountedAmount = amount – discount;
if( discountedAmount <
0.0f )
{
discountedAmount
= 0.0f;
}
LogManager.Publish(
discountedAmount.ToString() );
// Return the discounted
amount
return discountedAmount;
}
2.1.4 White space
Liberal use of white space
is highly encouraged. This provides enhanced readability and is extremely
helpful during debugging and code reviews. The indentation example above shows
an example of the appropriate level of white space.
Guidelines:
o
Blank lines should be used to separate logical blocks of code in much
the way a writer separates prose using headings and paragraphs. Note the clean
separation between logical sections in the previous code example via the leading
comments and the blank lines immediately following.
o Single spaces should be used
to separate logical elements within individual statements. This can be seen
clearly in the CalculateDiscountedAmount
and switch statements in the preceding
example. Note the spaces immediately after opening ‘(‘s and before closing
‘)’s.
2.1.5 Long lines of code
Comments and statements that
extend beyond 80 columns in a single line can be broken up and indented for
readability. Care should be taken to ensure readability and proper
representation of the scope of the information in the broken lines. When
passing large numbers of parameters, it is acceptable to group related
parameters on the same line.
Example:
string Win32FunctionWrapper(
int arg1,
string arg2,
bool arg3 )
{
// Perform a PInvoke call to
a win32 function,
// providing default values
for obscure parameters,
// to hide the complexity from
the caller
if( Win32.InternalSystemCall(
null,
arg1,
arg2,
Win32.GlobalExceptionHandler,
0, arg3,
null )
{
return “Win32 system call succeeded.”;
}
else
{
return “Win32 system call failed.”;
}
}
Guidelines:
o
When breaking parameter lists into multiple lines, indent each
additional line one tab further than the starting line that is being continued.
o
Group similar parameters on the same line when appropriate.
o
When breaking comments into multiple lines, match the indentation level
of the code that is being commented upon.
o Consider embedding large
string constants in resources and retrieving them dynamically using the .NET
ResourceManager
class.
2.1.6 Commenting
2.1.6.1 Intellisense Comments
Use triple slash ‘///’ comments for documenting the public interface of
each class. This will allow Visual Studio.Net to pick up the method’s
information for Intellisense. These comments are required before each public,
internal, and protected class member and optional for private members.
2.1.6.2 End-Of-Line Comments
Use End-Of-Line comments only with variable
and member field declarations. Use them to document the purpose of the variable
being declared.
Example:
private
string name =
string.Empty; // Name of control (defaults to blank)
2.1.6.3 Single Line Comments
Use single line comments above each block of code relating to a
particular task within a method that performs a significant operation or when a
significant condition is reached. Comments should always begin with two
slashes, followed by a space.
Example:
// Compute total price including all taxes
float stateSalesTax
= this.CalculateStateSalesTax( amount,
Customer.State );
float citySalesTax
= this.CalculateCitySalesTax(
amount, Customer.City );
float localSalesTax
= this.CalculateLocalSalesTax( amount,
Customer.Zipcode );
float
totalPrice = amount + stateSalesTax +
citySalesTax + localSalesTax;
Console.WriteLine( “Total
Price: {0}”, totalPrice );
2.1.6.4 // TODO: Comments
Use the // TODO: comment to mark a section of code that
needs further
work before release. Source code should be searched for these comments before
each release build.
2.1.6.5 C-Style Comments
Use c-style /*…*/ comments only for temporarily blocking out
large sections of code during development and debugging. Code should not be
checked in with these sections commented out. If the code is no longer
necessary, delete it. Leverage your source control tools to view changes and
deletions from previous versions of the code. If code must be checked in with
large sections commented out, include a // TODO: comment above the block commented out describing
why it was checked in that way.
Programming
o
Never declare more than 1 namespace
per file.
o
Avoid putting multiple classes in a
single file.
o
Always place curly braces ({
and }) on a new line.
o
Always use curly braces ({
and }) in conditional statements.
o
Always use a Tab & Indention
size of 4.
o
Declare each variable independently
– not in the same statement.
o
Place namespace “using” statements
together at the top of file. Group .NET namespaces above custom namespaces.
o
Group internal class implementation
by type in the following order:
·
Member
variables.
·
Constructors
& Finalizers.
·
Nested
Enums, Structs, and Classes.
·
Properties
·
Methods
o
Sequence declarations within type
groups based upon access modifier and visibility:
·
Public
·
Protected
·
Internal
·
Private
o
Segregate interface Implementation
by using #region statements.
o
Append folder-name to namespace for
source files within sub-folders.
o
Recursively indent all code blocks
contained within braces.
o
Use white space (CR/LF, Tabs, etc)
liberally to separate and organize code.
o
Only declare related attribute
declarations on a single line, otherwise stack each attribute as a separate
declaration.
Example:// Bad![Attrbute1, Attrbute2, Attrbute3] public class MyClass{…}// Good![Attrbute1, RelatedAttribute2] [Attrbute3][Attrbute4]public class MyClass {…}
o
Place Assembly scope attribute
declarations on a separate line.
o
Place Type scope attribute
declarations on a separate line.
o
Place Method scope attribute
declarations on a separate line.
o
Place Member scope attribute
declarations on a separate line.
o
Place Parameter attribute
declarations inline with the parameter.
o
If in doubt, always err on the side
of clarity and consistency.
2.1.7 Namespaces
Namespaces
represent the logical packaging of component layers and subsystems. The
declaration template for namespaces is: CompanyName.ProjectOrDomainName.PackageName.SubsystemName.
Examples:
Microsoft.Data.DataAccess
Microsoft.Logging.Listeners
Guidelines:
o Project
and package level namespaces will normally be predetermined by an application architect
for each project.
o Use
Pascal casing when naming Subsystem namespaces.
2.1.8 Classes & Structures
Classes
and structures represent the ‘Nouns’ of a system. As such, they should be
declared using the following template: Noun + Qualifier(s). Classes and structures should declared with
qualifiers that reflect their derivation from a base class whenever
possible.
Examples:
CustomerForm : Form
CustomerCollection : CollectionBase
Guidelines:
o Use
Pascal casing when naming classes and structures.
o Classes
and structures should be broken up distinct #regions as previously described in the class layout
guidelines.
o All
public classes and their methods should be documented using the Intellisense triple
slash ‘///’ comments built into Visual
Studio.Net. Use this comment style to document the purpose of the class and its
methods.
o Default
values for fields should be assigned on the line where the field is declared.
These values are assigned at runtime just before the constructor is called.
This keeps code for default values in one place, especially when a class
contains multiple constructors.
2.1.9 Interfaces
Interfaces
express behavior contracts that derived classes must implement. Interface names
should use Nouns, Noun Phrases, or Adjectives that clearly express the behavior
that they declare.
Examples:
IComponent
IFormattable
ITaxableProduct
Guidelines:
o Prefix
interface names with the letter ‘I’.
o Use Pascal
casing when naming interfaces.
2.1.10 Constants
Constants
and static read-only variables should be declared using the following template:
Adjective(s) + Noun + Qualifier(s)
Example:
public const int DefaultValue
= 25;
public static
readonly string DefaultDatabaseName = “Membership”;
Guidelines:
o Use Pascal
casing when naming constants and static read only variables.
o Prefer
the use of static readonly over const for public constants whenever possible. Constants
declared using const are substituted
into the code accessing them at compile time. Using static readonly variables ensures that constant values are accessed at
runtime. This is safer and less prone to breakage, especially when accessing a constant
value from a different assembly.
2.1.11 Enumerations
Enumerations should be declared using the following template: Adjective(s) + Noun + Qualifier(s)
Example:
/// <summary>
/// Enumerates the ways a customer may purchase goods.
/// </summary>
[Flags]
public enum PurchaseMethod
{
All = ~0,
None = 0,
Cash = 1,
Check = 2,
CreditCard = 4,
DebitCard = 8,
Voucher =
16,
}
Guidelines:
o Use
Pascal casing when naming enumerations.
o Use
the [Flags]
attribute only to indicate that the
enumeration can be treated as a bit field; that is, a set of flags.
2.1.12 Variables, Fields & Parameters
Variables, fields, and
parameters should be declared using the following template: Adjective(s) + Noun + Qualifier(s)
Examples:
int lowestCommonDenominator = 10;
float firstRedBallPrice =
25.0f;
Guidelines:
o Use
Camel casing when naming variables, fields, and parameters.
o Define
variables as close as possible to the first line of code where they are used.
o Declare
each variable and field on a separate line. This allows the use of End-Of-Line
comments for documenting their purpose.
o Assign
initial values whenever possible. The .NET runtime defaults all unassigned
variables to 0 or null automatically, but assigning them proper values will
alleviate unnecessary checks for proper assignment elsewhere in code.
o Avoid
meaningless names like i, j, k,
and temp. Take the time to describe what the object
really is (e.g. use index
instead of i;
use swapInt instead
of tempInt).
o Use
a positive connotation for boolean variable names (e.g. isOpen as opposed to notOpen).
o
Try to initialize variables where
you declare them.
o
Always choose the simplest data
type, list, or object required.
o
Always use the built-in C# data type
aliases, not the .NET common type system (CTS).
Example:
short NOT System.Int16
int NOT System.Int32
long NOT System.Int64
string NOT System.String
o
Only declare member variables as private.
Use properties to provide access to them with public,
protected, or
internal access
modifiers.
o
Try to use int
for any non-fractional numeric values that will fit the int
datatype - even variables for non-negative numbers.
o
Only use long
for variables potentially containing values too large for an int.
o
Try to use double
for fractional numbers to ensure decimal precision in calculations.
o
Only use float
for fractional numbers that will not fit double or decimal.
o
Avoid
using float unless you fully understand the
implications upon any calculations.
o
Try to use decimal
when fractional numbers must be rounded to a fixed precision for calculations.
Typically this will involve money.
o
Avoid using sbyte,
short,
uint,
and ulong unless it is for interop (P/Invoke)
with native libraries.
o Avoid
specifying the type for an enum - use the default of int
unless you have an explicit need for long (very uncommon).
o
Avoid
using inline numeric literals (magic numbers). Instead, use a Constant
or Enum.
o
Avoid declaring string literals
inline. Instead use Resources, Constants, Configuration Files, Registry or
other data sources.
o
Declare readonly
or static readonly variables instead of constants for
complex types.
o
Only declare constants
for simple types.
o
Avoid direct casts. Instead, use the
“as”
operator and check for null.
Example:
object dataObject = LoadData();
DataSet ds = dataObject as
DataSet;
if(ds
!= null) {…}
o
Always prefer C# Generic collection
types over standard or strong-typed collections. [C#v2+]
o
Always explicitly initialize arrays
of reference types using a for loop.
o
Avoid boxing and unboxing value
types.
Example:
int count
= 1;
object refCount
= count; // Implicitly boxed.
int newCount
= (int)refCount; // Explicitly unboxed.
o
Floating point values should include
at least one digit before the decimal place and one after.
Example: totalPercent = 0.05;
o
Try to use the “@”
prefix for string literals instead of escaped strings.
o
Prefer String.Format()
or StringBuilder over string concatenation.
o
Never concatenate strings inside a
loop.
o
Do not compare strings to String.Empty
or “” to check for empty strings.
Instead, compare by using String.Length == 0.
o
Avoid hidden string allocations
within a loop. Use String.Compare() for case-sensitive
Example:
(ToLower()
creates a temp string)
int id = -1;
string name = “Sample String”;
for(int i=0;
i < customerList.Count; i++)
{
if(customerList[i].Name.ToLower() == name)
{
id = customerList[i].ID;
}
}
// Good!
int id = -1;
string name = “lance hunt”;
for(int i=0;
i < customerList.Count; i++)
{
// The “ignoreCase = true” argument performs a
// case-insensitive compare without new allocation.
if(String.Compare(customerList[i].Name,
name, true)== 0)
{
id = customerList[i].ID;
}
}
2.1.13 Properties
Properties should be declared using the following template: Adjective(s) + Noun + Qualifier(s)
Examples:
public TotalPrice
{
get
{
return this.totalPrice;
}
set
{
// Set value and fire changed event if new value is
different
if( !object.Equals( value,
this.totalPrice )
{
this.totalPrice = value;
this.OnTotalPriceChanged();
}
}
}
Guidelines:
o
Do not omit access modifiers.
Explicitly declare all identifiers with the appropriate access modifier instead
of allowing the default.
Example:
// Bad!
Void WriteEvent(string
message) {…}
// Good!
private Void WriteEvent(string
message) {…}
o
Do not use the default (“1.0.*”)
versioning scheme . Increment the AssemblyVersionAttribute value manually.
o
Set the ComVisibleAttribute
to false for all assemblies.
o
Only selectively enable the ComVisibleAttribute
for individual classes when needed.
Example:
[assembly:
ComVisible(false)]
[ComVisible(true)] public MyClass
{…}
o
Consider factoring classes
containing unsafe code blocks into a separate
assembly.
o Avoid
mutual references between assemblies.
o When
there is a property setter that sets another property:
§ If
the code in the other property sets a private member field in the same class,
the field should be set directly, without calling the property setter for that
field.
§ If
a property setter sets a private field that would normally be set via another
property setter, the originating setter is responsible for firing any events the
other setter would normally fire (e.g. Changed events).
§ If
a value that needs to be set that does NOT correspond to a private field, then an
appropriate property setter or method should be called to set the value.
2.1.14 Methods
Methods should be named using the following format: Verb + Adjective(s)
+ Noun + Qualifier(s)
Example:
private Ball FindRedCansByPrice(
float price,
ref int canListToPopulate,
out int numberOfCansFound )
Guidelines:
o Parameters
should be grouped by their mutability (from least to most mutable) as shown in
the example above.
o If
at all possible, avoid exiting methods from their middles. A well written method should only exit from
one point: at its end.
o Avoid
large methods. As a method’s body approaches 20 to 30 lines of code, look for blocks
that could be split into their own methods and possibly shared by other
methods.
o If
you find yourself using the same block of code more than once, it’s a good
candidate for a separate method.
o Group
like methods within a class together into a region and order them by frequency
of use (i.e. more frequently called methods should be near the top of their regions.
2.1.15 Event Handlers
Event handlers should be declared using the following format: ObjectName_EventName
Example:
private HelpButton_Click( object
sender, EventArgs e )
2.1.16 Error Handling
Use
exceptions only for exceptional cases, not for routine program flow. Exceptions
have significant performance overhead.
Guidelines:
o
Pass a descriptive string into the constructor when throwing an
exception.
o
Use grammatically correct error messages, including ending punctuation.
Each sentence in the description string of an exception should end in a period.
o
If a property or method throws an exception in some cases, document
this in the comments for the method. Include which exception is thrown and what
causes it to be thrown.
·
Example: Comment for Order.TotalCost property might read "Gets or
sets the total cost of an Order. If the TotalCost property is set when the cost
should be calculated, an InvalidOperationException
is thrown."
o Use the following exceptions
if appropriate:
·
ArgumentException
(and ArgumentNull, ArgumentOutOfRange, IndexOutOfRange): Used when checking for valid input
parameters to method.
·
InvalidOperationException: Used when a method call is invalid for the
current state of an object.
Example: TotalCost cannot be set if the cost
should be calculated. If the property is set and it fails this rule, an InvalidOperationException is
thrown.
·
NotSupportedException: Used when a method call is invalid for the
class.
Example: Quantity, a virtual read/write
property, is overridden by a derived class. In the derived class, the
property is read-only. If the property is set, a NotSupportedException is
thrown.
·
NotImplementedException: Used when a method is not implemented for
the current class.
Example: A
interface method is stubbed in and not yet implemented. This method should throw
a NotImplementedException.
o Derive your own exception
classes for a programmatic scenarios. All new derived exceptions should be
based upon the core Exception
class.
Example: DeletedByAnotherUserException
: Exception. Thrown to indicate a record being modified has been
deleted by another user.
o
Rethrow caught exceptions correctly.
The
following example throws an exception caught and rethrown incorrectly:
catch( Exception ex )
{
LogManager.Publish( ex );
throw ex;// INCORRECT – we lose the call stack of the exception
}
{
LogManager.Publish( ex );
throw ex;// INCORRECT – we lose the call stack of the exception
}
We log all unhandled exceptions in our applications,
but may sometimes throw them again to let the higher level systems determine
how to proceed. The problem comes in with the throw – it works much better to
do this:
catch( Exception ex )
{
LogManager.Publish( ex );
throw; // CORRECT - rethrows the exception we just caught
}
{
LogManager.Publish( ex );
throw; // CORRECT - rethrows the exception we just caught
}
Notice the absence of an argument to the throw
statement in the second variation.
The difference between these two variations is
subtle but important. With the first example, the higher level caller isn’t
going to get all the information about the original error. The call stack in
the exception is replaced with a new call stack that originates at the “throw
ex” statement – which is not what we want to
record. The second example is the only one that actually re-throws the original exception,
preserving the stack trace where the original error occurred.
o
Do not use try/catch
blocks for flow-control.
o
Only catch
exceptions that you can handle.
o
Never declare an empty catch block.
o
Avoid nesting a try/catch
within a catch block.
o
Always catch the most derived exception via
exception filters.
o
Order exception filters from most to least
derived exception type.
o
Avoid re-throwing an exception. Allow it to
bubble-up instead.
o
Only use the finally
block to release resources from a try statement.
o
Always use validation to avoid
exceptions.
Example:
// Bad!
try
{
conn.Close();
}
Catch(Exception
ex)
{
// handle exception if already closed!
}
// Good!
if(conn.State != ConnectionState.Closed)
{
conn.Close();
}
o
Always set the innerException
property on thrown exceptions so the exception chain & call stack are
maintained.
o
Avoid defining custom exception
classes. Use existing exception classes instead.
o
When a custom exception is required;
·
Always
derive from Exception not ApplicationException.
·
Always
suffix exception class names with the word “Exception”.
·
Always
add the SerializableAttribute to exception classes.
·
Always
implement the standard “Exception Constructor Pattern”:
public MyCustomException ();
public MyCustomException (string message);
public MyCustomException (string message,
Exception innerException);
·
Always
implement the deserialization constructor:
protected MyCustomException(SerializationInfo
info, StreamingContext contxt);
o
Always set the appropriate HResult
value on custom exception classes. (Note: the ApplicationException
HResult = -2146232832)
o
When defining custom exception
classes that contain additional properties:
·
Always
override the Message property, ToString()
method and the implicit operator string to include
custom property values.
·
Always
modify the deserialization constructor to retrieve custom property values.
·
Always override the GetObjectData(…)
method to add custom properties to the serialization collection.
Example:
public override void GetObjectData(SerializationInfo
info,StreamingContext
context)
{
base.GetObjectData
(info, context);
info.AddValue("MyValue",
_myValue);
}
2.1.17 Language Usage
2.1.17.1 Flow Control
o
Avoid invoking methods within a
conditional expression.
o
Avoid creating recursive methods.
Use loops or nested loops instead.
o
Avoid using foreach
to iterate over immutable value-type collections. E.g. String arrays.
o
Do not modify enumerated items
within a foreach statement.
o
Use the ternary conditional
operator only for trivial conditions. Avoid complex or compound ternary
operations.
Example: int result = isValid ?
9
: 4;
o
Avoid evaluating Boolean conditions
against true or false.
Example:
// Bad!
if (isValid ==
true) {…}
// Good!
if (isValid) {…}
o
Avoid assignment within conditional
statements.
Example: if((i=2)==2)
{…}
o
Avoid compound conditional
expressions – use Boolean variables to split parts into multiple manageable
expressions.
Example:
// Bad!
if (((value > _highScore) && (value != _highScore)) && (value < _maxScore)) {…}
// Good!
isHighScore =
(value >= _highScore); isTiedHigh = (value ==
_highScore); isValid = (value < _maxValue);
if ((isHighScore
&& ! isTiedHigh) && isValid) {…}
o
Avoid explicit Boolean tests in
conditionals.
Example:
// Bad!
if(IsValid ==
true) {…};
// Good!
if(IsValid) {…}
o
Only use switch/case
statements for simple operations with parallel conditional logic.
o
Prefer nested if/else
over switch/case for short conditional sequences and
complex conditions.
o
Prefer polymorphism over switch/case
to encapsulate and delegate complex operations.
2.1.17.2 Events, Delegates, & Threading
o
Always check Event & Delegate
instances for null before invoking.
o
Use the default EventHandler
and EventArgs for most simple events.
o
Always derive a custom EventArgs
class to provide additional data.
o
Use the existing CancelEventArgs
class to allow the event subscriber to control events.
o
Always use the “lock”
keyword instead of the Monitor type.
o
Only lock on a private or private
static object.
Example: lock(myVariable);
o
Avoid locking on a Type.
Example: lock(typeof(MyClass));
o
Avoid locking on the current object
instance.
Example: lock(this);
2.1.17.3 Object Composition
o
Always declare types explicitly
within a namespace. Do not use the default “{global}” namespace.
o
Avoid overuse of the public
access modifier. Typically fewer than 10% of your types and members will be
part of a public API, unless you are writing a class library.
o
Consider using internal
or private access modifiers for types and
members unless you intend to support them as part of a public API.
o
Never use the protected
access modifier within sealed classes unless overriding a protected
member of an inherited type.
o
Avoid declaring methods with more
than 5 parameters. Consider refactoring
this code.
o
Try to replace large parameter-sets
(> than 5 parameters) with one or more class
or struct parameters – especially when used
in multiple method signatures.
o
Do not use the “new”
keyword on method and property declarations to hide members of a derived type.
o
Only use the “base”
keyword when invoking a base class constructor or base implementation within an
override.
o
Consider using method overloading
instead of the params attribute (but be careful not to
break CLS Compliance of your API’s).
o
Always validate an enumeration
variable or parameter value before consuming it. They may contain any value
that the underlying Enum type (default int) supports.
Example:
public void Test(BookCategory
cat)
{
if (Enum.IsDefined(typeof(BookCategory),
cat)) {…}
}
o
Consider overriding Equals()
on a struct.
o
Always override the Equality Operator
(==)
when overriding the Equals() method.
o
Always override the String Implicit Operator when overriding the ToString()
method.
o
Always call Close()
or Dispose() on classes that offer it.
o
Wrap instantiation of IDisposable
objects with a “using” statement to ensure that Dispose()
is automatically called.
Example:
using(SqlConnection cn = new SqlConnection(_connectionString))
o
Always implement the IDisposable
interface & pattern on classes referencing external resources.
Example:
(shown
with optional Finalizer)
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if
(disposing)
{
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
// Set large fields to null.
}
// C# finalizer. (optional) ~Base()
{
// Simply call
Dispose(false). Dispose
(false);
}
o
Avoid implementing a Finalizer.
Never define a Finalize()
method as a finalizer. Instead use the C# destructor syntax.
Example
// Good ~MyClass {…}
// Bad
void Finalize(){…}
Object Model & API Design
o
Always prefer aggregation over inheritance.
o
Avoid “Premature Generalization”. Create
abstractions only when the intent is understood.
o
Do the simplest thing that works, then
refactor when necessary.
o
Always make object-behavior transparent to
API consumers.
o
Avoid unexpected side-affects when
properties, methods, and constructors are invoked.
o
Always separate presentation layer from
business logic.
o
Always prefer interfaces over abstract
classes.
o
Try to include the design-pattern names such
as “Bridge”, “Adapter”, or “Factory” as a suffix to clas s names where
appropriate.
o
Only make members virtual
if they are designed and tested for extensibility.
o
Refactor often
Appendix A. Naming Parts & Pairs
A.1 Common Adjective Pairs A.2 Common Property Prefixes
Old…/New…
|
Allow…
(Allows…)
|
|
Source…/Destination…
|
Can…
|
|
Source…/Target…
|
Contains…
|
|
First…/Next…/Current…/Previous…/Last…
|
Has…
|
|
Min…/Max…
|
Is…
|
|
Use… (Uses…)
|
A.3 Common Verb Pairs
Add…/Remove…
|
Open…/Close…
|
Insert…/Delete…
|
Create…/Destroy…
|
Increment/…Decrement…
|
Acquire…/Release…
|
Lock…/Unlock…
|
Up…/Down…
|
Begin…/End…
|
Show…/Hide…
|
Fetch…/Store…
|
Start…/Stop…
|
To…/From… (Convert implied)
|
A.4 Common Qualifiers Suffixes
…Avg
|
…Limit
|
…Count
|
…Ref
|
…Entry
|
…Sum
|
…Index
|
…Total
|
Note: Avoid
using Num because of semantics; use Index and Count instead. Also, avoid using Temp; take the time to describe what the object really is (e.g. use
SwapValue instead of TempValue).
The following references were used to develop the
guidelines described in this document:
o
MSDN: .NET Framework Developer’s Guide: Common
Type System”, Microsoft Corporation, 2004, http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconthecommontypesystem.asp
o
“MSDN: C# Language Specification v1.5”, Scott
Wiltamuth & Anders Hejlsberg, Microsoft Corporation, 2003, http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfcsharpspec_15.asp
o
“MSDN: Design Guidelines for Class Library
Developers”, Microsoft Corporation, 2004,
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconNETFrameworkDesignGuidelines.asp
o
“MSDN: The Well Tempered Exception”, Eric
Gunnerson, Microsoft Corporation, 2001
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp08162001.asp
o
“Applied Microsoft .NET Framework Programming”,
Jeffrey Richter, January 23, 2002, 1st ed., Microsoft Press, ISBN: 0735614229
o
“Which type should I use in C# to represent
numbers?”, locabol, February 27, 2007, Luca Bolognese’s Weblog http://blogs.msdn.com/lucabol/archive/2007/02/27/which-type-should-i-use-in-c-to-represent-numbers.aspx
o
Code Complete - McConnell
o
Writing Solid Code - Macguire
o
Practical Standards for Microsoft Visual Basic -
Foxall
o
The Elements of Java Style – Vermeulen, et. al.
o
The Elements of C++ Style – Misfeldt, et. al.