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 you may follow any of
them. What is more important is, selecting one standard approach and ensuring
that everyone is following it.
If you have a team of different skills and tastes, you are
going to have a tough time convincing everyone to follow the same standards.
The best approach is to have a team meeting and developing your own standards
document. You may use this document as a template to prepare your own document.
Distribute a copy of this document (or your own coding standard
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, you 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. (Don’t forget to appreciate the developer for the
good work and also make sure he does not get offended by the “group
attack”!)
·
Always
use multi-layer (N-Tier) architecture.
·
Never
access database from the UI pages. Always have a data layer class which
performs all the database related tasks. This will help you support or migrate
to another database back end easily.
·
Use
try-catch in your data layer to catch all database exceptions. This exception
handler should record all exceptions from the database. The details recorded
should include the name of the command being executed, stored proc name,
parameters, connection string used etc. After recording the exception, it could
be re thrown so that another layer in the application can catch it and take
appropriate action.
·
Separate
your application into multiple assemblies. Group all independent utility
classes into a separate class library. All your database related files can be
in another class library.
·
Do not use session variables throughout the code. Use
session variables only within the classes and expose methods to access the
value stored in the session variables. A class can access the session using System.Web.HttpCOntext.Current.Session
·
Do
not store large objects in session. Storing large objects in session may
consume lot of server memory depending on the number of users.
·
Always
use style sheet to control the look and feel of the pages. Never specify font
name and font size in any of the pages. Use appropriate style class. This will
help you to change the UI of your application easily in future. Also, if you
like to support customizing the UI for each customer, it is just a matter of
developing another style sheet for them.
1.
Never
do a 'catch exception and do nothing'. If you hide an exception, you will never
know if the exception happened or not. Lot of developers uses this handy method
to ignore non-significant errors. You should always try to avoid exceptions by
checking all the error conditions programmatically. In any case, catching an
exception and doing nothing is not allowed. In the worst case, you should log
the exception and proceed.
2.
In
case of exceptions, give a friendly message to the user, but log the actual
error with all possible details about the error, including the time it
occurred, method and class name etc.
3.
Always
catch only the specific exception, not generic exception.
Good:
void
ReadFromFile ( string fileName )
{
try
{
//
read from file.
}
catch
(FileIOException ex)
{
//
log error.
// re-throw exception depending on your case.
throw;
}
}
Not Good:
void ReadFromFile ( string fileName )
{
try
{
// read from file.
}
catch (Exception ex)
{
// Catching general exception is bad... we will never know whether
// it was a file error or some other error.
// Here you are hiding an exception.
// In this case no one will ever know that an exception happened.
return "";
}
}
4.
No
need to catch the general exception in all your methods. Leave it open and let
the application crash. This will help you find most of the errors during
development cycle. You can have an application level (thread level) error
handler where you can handle all general exceptions. In case of an 'unexpected
general error', this error handler should catch the exception and should log
the error in addition to giving a friendly message to the user before closing
the application, or allowing the user to 'ignore and proceed'.
5.
When
you re throw an exception, use the throw
statement without specifying the original exception. This way, the original
call stack is preserved.
Good:
catch
{
// do whatever you want to handle
the exception
throw;
}
Not Good:
catch
(Exception ex)
{
// do whatever you want to handle
the exception
throw ex;
}
6.
Do
not write try-catch in all your methods. Use it only if there is a possibility
that a specific exception may occur and it cannot be prevented by any other
means. For example, if you want to insert a record if it does not already
exists in database, you should try to select record using the key. Some
developers try to insert a record without checking if it already exists. If an
exception occurs, they will assume that the record already exists. This is
strictly not allowed. You should always explicitly check for errors rather than
waiting for exceptions to occur. On the other hand, you should always use
exception handlers while you communicate with external systems like network,
hardware devices etc. Such systems are subject to failure anytime and error
checking is not usually reliable. In those cases, you should use exception
handlers and try to recover from error.
7.
Do
not write very large try-catch blocks. If required, write separate try-catch
for each task you perform and enclose only the specific piece of code inside
the try-catch. This will help you find which piece of code generated the
exception and you can give specific error message to the user.
8.
Write
your own custom exception classes if required in your application. Do not
derive your custom exceptions from the base class SystemException. Instead,
inherit from ApplicationException.
9.
Do
not use try/catch blocks for flow-control.
10.
Only
catch exceptions that you can handle.
11.
Never
declare an empty catch block.
12.
Avoid nesting a try/catch within a catch
block.
13.
Always
catch the most derived exception via exception filters.
14.
Order
exception filters from most to least derived exception type.
15.
Avoid
re-throwing an exception. Allow it to bubble-up instead.
16.
If
re-throwing an exception, preserve the original call stack by omitting the
exception argument from the throw statement.
Example: // Bad!
catch(Exception
ex) { Log(ex); throw ex; }
//Good!
catch(Exception) { Log(ex); throw; }
17.
Only
use the finally block to release resources from a try statement.
18.
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(); }
19 a. Always set the innerException
property on thrown exceptions so the exception chain & call stack are
maintained.
19 b.
Avoid
defining custom exception classes. Use existing exception classes instead.
20.
When
a custom exception is required;
a.
Always
derive from Exception not ApplicationException.
b.
Always
suffix exception class names with the word “Exception”.
c.
Always
add the SerializableAttribute to exception classes.
d.
Always
implement the standard “Exception Constructor Pattern”: public
MyCustomException (); public MyCustomException (string message); public
MyCustomException (string message, Exception innerException);
e.
Always
implement the deserialization constructor: protected
MyCustomException(SerializationInfo info, StreamingContext contxt);
21.
Always
set the appropriate HResult value on custom exception classes. (Note: the
ApplicationException HResult = -2146232832)
22.
When
defining custom exception classes that contain additional properties:
a.
Always
override the Message property, ToString() method and the implicit operator
string to include custom property values.
b.
Always
modify the deserialization constructor to retrieve custom property values.
c.
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); }
Object
Model & API Design
·
Always
prefer aggregation over inheritance.
·
Avoid
“Premature Generalization”. Create abstractions only when the intent is
understood.
·
Do
the simplest thing that works, then refactor when necessary.
·
Always
make object-behavior transparent to API consumers.
·
Avoid
unexpected side-affects when properties, methods, and constructors are invoked.
·
Always
separate presentation layer from business logic.
·
Always
prefer interfaces over abstract classes.
·
Try
to include the design-pattern names such as “Bridge”, “Adapter”, or “Factory”
as a suffix to class names where appropriate.
·
Only
make members virtual if they are designed and tested for extensibility.
·
Refactor
often!
Object Composition
1.
Always
declare types explicitly within a namespace. Do not use the default “{global}”
namespace. 69. 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.
2.
Consider
using internal or private access modifiers for types and members unless you
intend to support them as part of a public API.
3.
Never
use the protected access modifier within sealed classes unless overriding a
protected member of an inherited type.
4.
Avoid
declaring methods with more than 5 parameters. Consider refactoring this code.
5.
Try
to replace large parameter-sets (> than 5 parameters) with one or more class
or struct parameters – especially when used in multiple method signatures.
6.
Do
not use the “new” keyword on method and property declarations to hide members
of a derived type.
7.
Only
use the “base” keyword when invoking a base class constructor or base
implementation within an override.
8.
Consider
using method overloading instead of the params attribute (but be careful not to
break CLS Compliance of your API’s).
9.
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)) {…} }
10. Consider overriding
Equals() on a struct.
11. Always override the
Equality Operator (==) when overriding the Equals() method.
12. Always override the
String Implicit Operator when overriding the ToString() method.
13. Always call Close() or
Dispose() on classes that offer it.
14. Wrap instantiation of
IDisposable objects with a “using” statement to ensure that Dispose() is
automatically called.
Example:
using(SqlConnection cn = new SqlConnection(_connectionString)) {…}
15. 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); }
16. Avoid implementing a
Finalizer. Never define a Finalize() method as a finalizer. Instead use the C#
destructor syntax.
Example // Good ~MyClass {…} // Bad void
Finalize(){…}
Below are some examples of our C# coding standards, naming
conventions, and best practices. Use these according to your own needs.
C# Coding Conventions (C#
Programming Guide)
The C# Language
Specification does not
define a coding standard. However, the guidelines in this topic are used by
Microsoft and non-Microsoft to develop samples and documentation.
Coding
conventions serve the following purposes:
·
They create a consistent look to the code, so that readers can
focus on content, not layout.
·
They enable readers to understand the code more quickly by
making assumptions based on previous experience.
·
They facilitate copying, changing, and maintaining the code.
·
They demonstrate C# best practices.
Naming Conventions
·
In short examples that do not include using
directives, use namespace qualifications. If you know that a namespace is
imported by default in a project, you do not have to fully qualify the names
from that namespace. Qualified names can be broken after a dot (.) if they are
too long for a single line, as shown in the following example.
C#
var currentPerformanceCounterCategory = new System.Diagnostics.
PerformanceCounterCategory();
·
You do not have to change the names of objects that were created
by using the Visual Studio designer tools to make them fit other guidelines.
·
Move Declaration Near
Reference
Some programmers are used to
placing temporary variable declarations at the top of the method.But by placing
the variable declaration far away from the line where it is first referenced,
you are making code more difficult to read, because it is not immediately clear
how the variable was initialized. In addition, declaring variables well ahead
of time makes the method more difficult to refactor. If you try to extract a
part of the method in which the variable was referenced, you have to pass the
variable as a parameter to a newly extracted method, even though the
declaration could possibly have been placed inside the extracted block.
·
Do use PascalCasing for class
names and method names.
I am providing some basic level examples below but as I
already mentioned, find a good convention that fits for you and stick with it.
public class Product
{
public void GetActiveProducts()
{
//...
}
public void CalculateProductAdditinalCost()
{
//...
}
}
Do use camelCasing
for method arguments and local variables.
public class ProductCategory
{
public void Save(ProductCategory productCategory)
{
// ...
}
}
Do not use Abbreviations.
// Correct
ProductCategory
productCategory;
// Avoid
ProductCategory
prodCat;
Do not use Underscores in
identifiers.
// Correct
ProductCategory productCategory;
// Avoid
ProductCategory product_Category;
Do prefix
interfaces with the letter I.
public interface IAddress
{
}
Do declare all
member variables at the top of a class, with static variables at the very top.
public class Product
{
public static string
BrandName;
public string Name{get;
set;}
public DateTime DateAvailable {get; set;}
public Product()
{
// ...
}
}
Do use singular
names for enums. Exception: bit field enums.
public enum Direction
{
North,
East,
South,
West
}
Do not suffix enum names with
Enum.
//Avoid
public enum DirectionEnum
{
North,
East,
South,
West
}
General
Guidelines
·
Always
use Camel Case or Pascal Case names.
·
Avoid
ALL CAPS and all lowercase names. Single lowercase words or letters are
acceptable.
·
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.
·
Do
not use names that begin with a numeric character.
·
Do
add numeric suffixes to identifier names.
·
Always
choose meaningful and specific names.
·
Always
err on the side of verbosity not terseness.
·
Variables
and Properties should describe an entity not the type or size.
·
Do
not use Hungarian Notation!
Example: strName or iCount
·
Avoid
using abbreviations unless the full name is excessive. 11. Avoid abbreviations
longer than 5 characters.
·
Any
Abbreviations must be widely known and accepted.
·
Use
uppercase for two-letter abbreviations, and Pascal Case for longer
abbreviations.
·
Do
not use C# reserved words as names.
·
Avoid
naming conflicts with existing .NET Framework namespaces, or types.
·
Avoid
adding redundant or meaningless prefixes and suffixes to identifiers
Example: // Bad!
public enum ColorsEnum {…}
public class CVehicle {…}
public struct
RectangleStruct {…}
·
Do
not include the parent class name within a property name.
Example: Customer.Name
NOT Customer.CustomerName
·
Try
to prefix Boolean variables and properties with “Can”, “Is” or “Has”.
·
Append
computational qualifiers to variable names like Average, Count, Sum, Min, and
Max where appropriate.
·
When
defining a root namespace, use a Product, Company, or Developer Name as the
root. Example: LanceHunt.StringUtilities
- Namespace names should follow the standard pattern
<company
name>.<product name>.<top level module>.<bottom level
module>
- Use appropriate prefix for the UI elements so that you
can identify them from the rest of the variables.
There are 2 different
approaches recommended here.
·
Use
a common prefix ( ui_ ) for all UI elements. This will help you group all of
the UI elements together and easy to access all of them from the intellisense.
·
Use
appropriate prefix for each of the ui element. A brief list is given below.
Since .NET has given several controls, you may have to arrive at a complete
list of standard prefixes for each of the controls (including third party
controls) you are using.
Control
|
Prefix
|
Label
|
lbl
|
TextBox
|
txt
|
DataGrid
|
dtg
|
Button
|
btn
|
ImageButton
|
imb
|
Hyperlink
|
hlk
|
DropDownList
|
ddl
|
ListBox
|
lst
|
DataList
|
dtl
|
Repeater
|
rep
|
Checkbox
|
chk
|
CheckBoxList
|
cbl
|
RadioButton
|
rdo
|
RadioButtonList
|
rbl
|
Image
|
img
|
Panel
|
pnl
|
PlaceHolder
|
phd
|
Table
|
tbl
|
Validators
|
val
|
- File name should match with class name.
For example, for the
class HelloWorld, the file name should be helloworld.cs (or, helloworld.vb)
Layout Conventions
Good
layout uses formatting to emphasize the structure of your code and to make the
code easier to read. Microsoft examples and samples conform to the following
conventions:
·
Use the default Code Editor Settings (smart indenting,
four-character indents, tabs saved as spaces). Write only one statement per
line.
·
Write only one declaration per line.
·
If continuation lines are not indented automatically, indent
them one tab stop (four spaces).
·
Add at least one blank line between method definitions and
property definitions.
·
Use parentheses to make clauses in an expression apparent, as
shown in the following code.
C#
if ((val1 > val2) && (val1 > val3))
{
// Take appropriate action.
}
Indentation and Spacing
- Use TAB for indentation. Do not use SPACES. Define the Tab size as 4.
- Comments should be in the same level as the code (use
the same level of indentation).
Good:
// Format a message and display
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time
is : " + currentTime.ToShortTimeString();
MessageBox.Show ( message );
Not Good:
// Format a message and display
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time
is : " + currentTime.ToShortTimeString();
MessageBox.Show ( message );
- Curly braces ( {} )
should be in the same level as the code outside the braces.
- Use one blank line to separate logical groups of code.
Good:
bool SayHello ( string name )
{
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time
is : " + currentTime.ToShortTimeString();
MessageBox.Show ( message );
if ( ... )
{
// Do something
// ...
return false;
}
return true;
}
Not Good:
bool SayHello (string name)
{
string fullMessage = "Hello " + name;
DateTime currentTime = DateTime.Now;
string message = fullMessage + ", the time
is : " + currentTime.ToShortTimeString();
MessageBox.Show ( message );
if ( ... )
{
// Do something
// ...
return false;
}
return true;
}
- There should be one and only one single blank line
between each method inside the class.
- The curly braces should be on a separate line and not
in the same line as if,
for etc.
Good:
if ( ... )
{
// Do something
}
Not Good:
if ( ... ) {
// Do something
}
- Use a single space before and after each operator and
brackets.
Good:
if ( showResult == true )
{
for ( int i = 0; i < 10; i++ )
{
//
}
}
Not Good:
if(showResult==true)
{
for(int i= 0;i<10;i++)
{
//
}
}
- Use #region
to group related pieces of code together. If you use proper grouping using
#region, the
page should like this when all definitions are collapsed.
- Keep private member variables, properties and methods
in the top of the file and public members in the bottom.
Formatting Guidelines
·
Never
declare more than 1 namespace per file.
·
Avoid
putting multiple classes in a single file.
·
Always
place curly braces ({ and }) on a new line.
·
Always
use curly braces ({ and }) in conditional statements.
·
Always
use a Tab & Indention size of 4.
·
Declare
each variable independently – not in the same statement.
·
Place
namespace “using” statements together at the top of file. Group .NET namespaces
above custom namespaces.
·
Group
internal class implementation by type in the following order: a. Member
variables. b. Constructors & Finalizers. c. Nested Enums, Structs, and
Classes. d. Properties e. Methods
·
Sequence
declarations within type groups based upon access modifier and visibility: a.
Public b. Protected c. Internal d. Private
·
Segregate
interface Implementation by using #region statements.
·
Append
folder-name to namespace for source files within sub-folders.
·
Recursively
indent all code blocks contained within braces.
·
Use
white space (CR/LF, Tabs, etc) liberally to separate and organize code.
·
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
{…}
·
Place
Assembly scope attribute declarations on a separate line.
·
Place
Type scope attribute declarations on a separate line.
·
Place
Method scope attribute declarations on a separate line.
·
Place
Member scope attribute declarations on a separate line.
·
Place
Parameter attribute declarations inline with the parameter.
·
If
in doubt, always err on the side of clarity and consistency.
Commenting Conventions
·
Place
the comment on a separate line, not at the end of a line of code.
·
Begin
comment text with an uppercase letter.
·
End
comment text with a period.
·
Insert
one space between the comment delimiter (//) and the comment text, as shown in
the following example.
·
C#
·
// The following declaration creates a query. It does not
run
·
// the query.
·
Do
not create formatted blocks of asterisks around comments.
·
All
comments should be written in the same language, be grammatically correct, and
contain appropriate punctuation.
·
Use
// or /// but never /* … */
·
Do
not “flowerbox” comment blocks. Example: //
*************************************** // Comment block //
***************************************
·
Use
inline-comments to explain assumptions, known issues, and algorithm insights. Do not use inline-comments to explain obvious
code. Well written code is self-documenting.
·
Only
use comments for bad code to say “fix this code” – otherwise remove, or rewrite
the code!
·
Include
comments using Task-List keyword flags to allow comment-filtering. Example: //
TODO: Place Database Code Here // UNDONE: Removed P\Invoke Call due to errors
// HACK: Temporary fix until able to refactor
·
Always
apply C# comment-blocks (///) to public, protected, and internal declarations. Only
use C# comment-blocks for documenting the API.
·
Always
include comments. Include, and comment sections where applicable.
·
Include
and where possible.
·
Always
add CDATA tags to comments containing code and other embedded markup in order
to avoid encoding issues. Example: /// /// Add the following key to the “appSettings”
section of your config.
·
Documentation Comments:
In the
.net framework, Microsoft has introduced a documentation generation system
based on XML comments. These comments are formally single line C♯ comments
containing XML tags. They follow this pattern for single line comments:
/// <summary>
/// This class...
///<summary>
·
Multiline
XML comments follow this pattern:
/// <exception cref=”BogusException”>
/// this exception gets thrown
as soon as a
/// bogus flag gets set.
///<exception>
·
All
lines must be preceded by three slashes to be accepted as XML comment lines.
Tags fall into two categories:
• Documentation items
• Formatting/Referencing
·
The first category contains tags like ,
<summary>,<param>or<exception> . These represent items that
represent the elements of a program's API which must be documented for the
program to be useful to other programmers. These tags usually have attributes such
as name or cref as demonstrated in the multiline example above. These
attributes are checked by the compiler, so they should be valid. The latter
category governs the layout of the documentation, using tags such as
<code>, and <list>
or <para>.
·
Documentation can then be generated using the
'documentation' item in the #develop 'build' menu. The documentation generated
is in HTMLHelp format. For a fuller explanation of XML comments see the
Microsoft .net framework SDK documentation. For information on commenting best
practice and further issues related to commenting, see the TechNote 'The fine
Art of Commenting'.
·
Single Line Comments You should use the //
comment style to "comment out" code (SharpDevelop has a key for it,
Alt+/). It may be used for commenting sections of code too. Single line
comments must be indented to the indent level when they are used for code
documentation. Commented out code should be commented out in the first line to
enhance the visibility of commented out code. A rule of thumb says that
generally, the length of a comment should not exceed the length of the code
explained by too much, as this is an indication of too complicated, potentially
buggy, code.
No comments:
Post a Comment