.
C#Coding Standards for .NET
1.3
Terminology & 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 { … }
Magic
Number
Any numeric literal
used within an expression (or to initialize a variable) that does not have an
obvious or wellknown
meaning. This usually
excludes the integers 0 or 1 and any other numeric equivalent precision that
evaluates as zero.
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.”
1.4
Flags
The following flags
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.
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
3
Quick Summary
This section contains
tables describing a high-level summary of the major standards covered in this
document. These
tables are not
comprehensive, but give a quick glance at commonly referenced elements.
1.4.1
Naming Conventions
“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
1.4.2
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.
Lance
Hunt C# Coding Standards for .NET
4 http://www.lance-hunt.net
1.4.3
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 7 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.
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
Avoid.
Use the C#
Destructors.
Do not create Finalize()
method.
AssemblyVersion
Increment manually.
ComVisibleAttribute
Set to false for all assemblies.
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
5
2.
Naming Conventions
Consistency is the
key to maintainable code. This statement is most true for naming your projects,
source files, and
identifiers including
Fields, Variables, Properties, Methods, Parameters, Classes, Interfaces, and
Namespaces.
2.1
General Guidelines
1. Always use Camel
Case or Pascal Case names.
2. Avoid ALL CAPS and
all lowercase names. Single lowercase words or letters are acceptable.
3. 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.
4. Do not use names
that begin with a numeric character.
5. Do add numeric
suffixes to identifier names.
6. Always choose
meaningful and specific names.
7. Always err on the
side of verbosity not terseness.
8. Variables and
Properties should describe an entity not the type or size.
9. Do not use
Hungarian Notation!
Example: strName
or iCount
10. Avoid using
abbreviations unless the full name is excessive.
11. Avoid abbreviations
longer than 5 characters.
12. Any Abbreviations
must be widely known and accepted.
13. Use uppercase for
two-letter abbreviations, and Pascal Case for longer abbreviations.
14. Do not use C#
reserved words as names.
15. Avoid naming
conflicts with existing .NET Framework namespaces, or types.
16. Avoid adding
redundant or meaningless prefixes and suffixes to identifiers
Example:
// Bad!
public enum ColorsEnum
{…}
public class CVehicle
{…}
public struct RectangleStruct
{…}
17. Do not include
the parent class name within a property name.
Example:
Customer.Name NOT Customer.CustomerName
18. Try to prefix
Boolean variables and properties with “Can”, “Is” or
“Has”.
19. Append
computational qualifiers to variable names like Average, Count, Sum, Min,
and Max where
appropriate.
20. When defining a
root namespace, use a Product, Company, or Developer Name as the root. Example:
LanceHunt.StringUtilities
Lance
Hunt C# Coding Standards for .NET
6 http://www.lance-hunt.net
2.2
Name Usage & Syntax
Identifier
Naming Convention
Project
File Pascal Case.
Always match Assembly
Name & Root Namespace.
Example:
LanceHunt.Web.csproj
-> LanceHunt.Web.dll -> namespace
LanceHunt.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
or
Embedded
File
Try to use Pascal
Case.
Use a name describing
the file contents.
Namespace
Pascal Case.
Try to partially
match Project/Assembly Name.
Example:
namespace LanceHunt.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
{…}
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
7
Generic
Class
&
Generic
Parameter
Type
[C#v2+]
Always use a single
capital letter, such as T or K.
Example:
public class FifoStack<T>
{
public void Push(<T>
obj)
{…}
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
(Public, Protected,
or Internal)
Pascal Case.
Avoid using
non-private Fields!
Use Properties instead.
Example:
public string Name;
protected IList InnerList;
Field
(Private) Camel Case and prefix with a single underscore (_)
character.
Example:
private string _name;
Constant
or
Static
Field
Treat like a 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
}
Lance
Hunt C# Coding Standards for .NET
8 http://www.lance-hunt.net
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)
{…}
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
9
3.
Coding Style
Coding style causes
the most inconsistency and controversy between developers. Each developer has a
preference, and
rarely are two the
same. However, consistent layout, format, and organization are key to creating
maintainable code.
The following
sections describe the preferred way to implement C# source code in order to
create readable, clear, and
consistent code that
is easy to understand and maintain.
3.1
Formatting
1. Never declare more
than 1 namespace per file.
2. Avoid putting
multiple classes in a single file.
3. Always place curly
braces ({ and }) on a new line.
4. Always use curly
braces ({ and }) in conditional statements.
5. Always use a Tab
& Indention size of 4.
6. Declare each
variable independently – not in the same statement.
7. Place namespace “using”
statements together at the top of file. Group .NET namespaces above custom
namespaces.
8. 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
9. Sequence
declarations within type groups based upon access modifier and visibility:
a. Public
b. Protected
c. Internal
d. Private
10. Segregate
interface Implementation by using #region statements.
11. Append
folder-name to namespace for source files within sub-folders.
12. Recursively
indent all code blocks contained within braces.
13. Use white space
(CR/LF, Tabs, etc) liberally to separate and organize code.
14. 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
{…}
15. Place Assembly
scope attribute declarations on a separate line.
16. Place Type scope attribute
declarations on a separate line.
17. Place Method
scope attribute declarations on a separate line.
18. Place Member
scope attribute declarations on a separate line.
19. Place Parameter attribute
declarations inline with the parameter.
20. If in doubt,
always err on the side of clarity and consistency.
Lance
Hunt C# Coding Standards for .NET
10 http://www.lance-hunt.net
3.2
Code Commenting
21. All comments
should be written in the same language, be grammatically correct, and contain
appropriate
punctuation.
22. Use // or /// but
never /* … */
23. Do not
“flowerbox” comment blocks.
Example:
//
***************************************
// Comment block
//
***************************************
24. Use
inline-comments to explain assumptions, known issues, and algorithm insights.
25. Do not use
inline-comments to explain obvious code. Well written code is self documenting.
26. Only use comments
for bad code to say “fix this code” – otherwise remove, or rewrite the code!
27. 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
28. Always apply C#
comment-blocks (///) to public, protected, and internal declarations.
29. Only use C#
comment-blocks for documenting the API.
30. Always include <summary>
comments. Include <param>, <return>, and <exception>
comment
sections where
applicable.
31. Include <see
cref=””/> and <seeAlso cref=””/> where possible.
32. Always add CDATA
tags to comments containing code and other embedded markup in order to
avoid
encoding issues.
Example:
/// <example>
/// Add the
following key to the “appSettings” section of your config:
/// <code><![CDATA[
///
<configuration>
/// <appSettings>
/// <add
key=”mySetting” value=”myValue”/>
///
</appSettings>
///
</configuration>
/// ]]></code>
///
</example>
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
11
4.
Language Usage
4.1
General
1. 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)
{…}
2. Do not use the
default (“1.0.*”) versioning scheme. Increment the AssemblyVersionAttribute
value
manually.
3. Set the ComVisibleAttribute
to false for all assemblies.
4. Only selectively
enable the ComVisibleAttribute for individual classes when
needed.
Example:
[assembly:
ComVisible(false)]
[ComVisible(true)]
public MyClass
{…}
5. Consider factoring
classes containing unsafe code blocks into a separate assembly.
6. Avoid mutual
references between assemblies.
4.2
Variables & Types
7. Try to initialize
variables where you declare them.
8. Always choose the
simplest data type, list, or object required.
9. 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
10. Only declare member
variables as private. Use properties to provide access to them with public,
protected, or
internal access modifiers.
11. Try to use int for
any non-fractional numeric values that will fit the int datatype
- even variables for nonnegative
numbers.
12. Only use long for
variables potentially containing values too large for an int.
13. Try to use double
for fractional numbers to ensure decimal precision in
calculations.
14. Only use float
for fractional numbers that will not fit double
or decimal.
15. Avoid using float
unless you fully understand the implications upon any
calculations.
16. Try to use decimal
when fractional numbers must be rounded to a fixed precision for
calculations. Typically
this will involve
money.
17. Avoid using sbyte, short, uint, and
ulong unless it is for interop (P/Invoke) with native libraries.
Lance
Hunt C# Coding Standards for .NET
12 http://www.lance-hunt.net
18. Avoid specifying
the type for an enum - use the default of int unless
you have an explicit need for long (very
uncommon).
19. Avoid using
inline numeric literals (magic numbers). Instead, use a Constant
or Enum.
20. Avoid declaring
string literals inline. Instead use Resources, Constants, Configuration Files,
Registry or other
data sources.
21. Declare readonly
or static readonly variables instead of constants for
complex types.
22. Only declare constants
for simple types.
23. Avoid direct
casts. Instead, use the “as” operator and check for null.
Example:
object dataObject
= LoadData();
DataSet ds =
dataObject as DataSet;
if(ds
!= null)
{…}
24. Always prefer C#
Generic collection types over standard or strong-typed collections. [C#v2+]
25. Always explicitly
initialize arrays of reference types using a for loop.
26. Avoid boxing and
unboxing value types.
Example:
int count
= 1;
object refCount
= count; // Implicitly boxed.
int newCount
= (int)refCount; // Explicitly unboxed.
27. Floating point
values should include at least one digit before the decimal place and one
after.
Example:
totalPercent = 0.05;
28. Try to use the “@”
prefix for string literals instead of escaped strings.
29. Prefer String.Format()
or StringBuilder over string concatenation.
30. Never concatenate
strings inside a loop.
31. Do not compare
strings to String.Empty or “” to check for empty strings. Instead, compare by using
String.Length == 0.
32. Avoid hidden
string allocations within a loop. Use String.Compare() for
case-sensitive
Example: (ToLower()
creates a temp string)
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
13
// Bad!
int id =
-1;
string name
= “lance hunt”;
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;
}
}
4.3
Flow Control
33. Avoid invoking
methods within a conditional expression.
34. Avoid creating
recursive methods. Use loops or nested loops instead.
35. Avoid using foreach
to iterate over immutable value-type collections. E.g. String
arrays.
36. Do not modify
enumerated items within a foreach statement.
37. Use the ternary
conditional operator only for trivial conditions. Avoid complex or compound
ternary operations.
Example:
int result = isValid ? 9 : 4;
38. Avoid evaluating
Boolean conditions against true or false.
Example:
// Bad!
if (isValid
== true)
{…}
// Good!
if (isValid)
{…}
39. Avoid assignment
within conditional statements.
Example:
if((i=2)==2) {…}
Lance
Hunt C# Coding Standards for .NET
14 http://www.lance-hunt.net
40. 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)
{…}
41. Avoid explicit
Boolean tests in conditionals.
Example:
// Bad!
if(IsValid
== true)
{…};
// Good!
if(IsValid)
{…}
42. Only use switch/case
statements for simple operations with parallel conditional logic.
43. Prefer nested if/else
over switch/case for short conditional sequences and complex conditions.
44. Prefer
polymorphism over switch/case to encapsulate and delegate complex operations.
4.4
Exceptions
45. Do not use try/catch
blocks for flow-control.
46. Only catch
exceptions that you can handle.
47. Never declare an
empty catch block.
48. Avoid nesting a try/catch
within a catch block.
49. Always catch the
most derived exception via exception filters.
50. Order exception
filters from most to least derived exception type.
51. Avoid re-throwing
an exception. Allow it to bubble-up instead.
52. 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;
}
53. Only use the finally
block to release resources from a try statement.
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
15
54. 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();
}
55. Always set the innerException
property on thrown exceptions so the exception chain & call
stack are
maintained.
56. Avoid defining
custom exception classes. Use existing exception classes instead.
57. 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);
58. Always set the
appropriate HResult value on custom exception classes.
(Note: the ApplicationException
HResult = -2146232832)
59. 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);
}
4.5
Events, Delegates, & Threading
60. Always check
Event & Delegate instances for null before
invoking.
61. Use the default EventHandler
and EventArgs for most simple events.
62. Always derive a
custom EventArgs class to provide additional data.
63. Use the existing CancelEventArgs
class to allow the event subscriber to control events.
64. Always use the “lock”
keyword instead of the Monitor type.
65. Only lock on a
private or private static object.
Example:
lock(myVariable);
Lance
Hunt C# Coding Standards for .NET
16 http://www.lance-hunt.net
66. Avoid locking on
a Type.
Example:
lock(typeof(MyClass));
67. Avoid locking on
the current object instance.
Example:
lock(this);
4.6
Object Composition
68. 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.
70. Consider using internal
or private access modifiers for types and members unless you intend to
support
them as part of a
public API.
71. Never use the protected
access modifier within sealed classes
unless overriding a protected member of
an inherited type.
72. Avoid declaring
methods with more than 5 parameters. Consider refactoring this code.
73. Try to replace
large parameter-sets (> than 5 parameters)
with one or more class or struct parameters –
especially when used
in multiple method signatures.
74. Do not use the “new”
keyword on method and property declarations to hide members of a derived type.
75. Only use the “base”
keyword when invoking a base class constructor or base implementation within an
override.
76. Consider using
method overloading instead of the params attribute
(but be careful not to break CLS
Compliance of your
API’s).
77. 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))
{…}
}
78. Consider
overriding Equals() on a struct.
79. Always override
the Equality Operator (==)
when overriding the Equals() method.
80. Always override
the String Implicit Operator when overriding the ToString()
method.
81. Always call Close()
or Dispose() on classes that offer it.
82. Wrap
instantiation of IDisposable objects with a “using” statement to ensure
that Dispose() is
automatically called.
Example:
using(SqlConnection
cn = new SqlConnection(_connectionString))
{…}
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
17
83. 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);
}
84. Avoid
implementing a Finalizer.
Never define a Finalize()
method as a finalizer. Instead use the C# destructor syntax.
Example
// Good
~MyClass {…}
// Bad
void Finalize(){…}
Lance
Hunt C# Coding Standards for .NET
18 http://www.lance-hunt.net
5.
Object Model & API Design
1. Always prefer
aggregation over inheritance.
2. Avoid “Premature
Generalization”. Create abstractions only when the intent is understood.
3. Do the simplest
thing that works, then refactor when necessary.
4. Always make
object-behavior transparent to API consumers.
5. Avoid unexpected
side-affects when properties, methods, and constructors are invoked.
6. Always separate presentation
layer from business logic.
7. Always prefer
interfaces over abstract classes.
8. Try to include the
design-pattern names such as “Bridge”, “Adapter”, or “Factory” as a suffix to
class names
where appropriate.
9. Only make members virtual
if they are designed and tested for extensibility.
10. Refactor often!
Lance
Hunt C# Coding Standards for .NET
http://www.lance-hunt.net
19
6.
References
“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
“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
“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
“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
“Applied Microsoft
.NET Framework Programming”, Jeffrey Richter, January 23, 2002, 1st ed., Microsoft
Press, ISBN:
0735614229
“Which type should I
use in C# to represent numbers?”, locabol, February 27, 2007, Luca Bolognese’s
Weblog
Code Layout
Use Visual Studio
Defaults - 4 character indentation - tabs saved as spaces
Source Code Layout
- One statement per line
- One declaration per line
Good:
int price;
int tax;
int total;
Bad:
int price, tax, total;
- separate method definitions
from property definitions ( 1 blank line )
- bracket on line following
statement:
Good:
public int returnInt()
{
// stuff
}
Bad:
public int returnInt(){
// stuff
}
Variables
use camel case for
fields and local variables:
string myString
Use Pascal casing for
all public member, type, and namespace names consisting of multiple words.
(From MSDN)
public class SampleClass
{
public Color BackColor
{
// Code for Get and Set accessors goes
here.
}
}
Use camel casing for
parameters:
public void
RemoveString(string ourString)
DO NOT use Hungarian
notation or language specific naming ( IntMyInteger or ConvertToShort )
Classes
A class or interface should have a single
purpose.
A class can represent a
primitive type or datastructure, abstraction or handle interaction between
other classes. But don't mix any of these things. Follow the "Single
Responsibility Principle" (from SOLID)
Use design patterns to
communicate the intent of the class.
Constructors
Constructors should only
be used to create a useful object. If you have too many paramaters in your
constructor your class may have too much responsibility.
Use a method instead of a property
Whenever you have a
property that:
- Contains logic
- performs more work than setting
a value
- returns a different value with
similar arguments (rand, guid, etc)
- Changest the state of another
property or object
Use a method instead.
Use Pascal casing
Miscellaneous
Throw exceptions not values
It's common for people
throw a status value, such as a plain text friendly message or a boolean value
to indicate success. Use the exception system to it's advantage. Catch the
exception and out output it to a debug console or log it in another way. It's
ok to append a friendly message or hint to what may have happened, but don't
rely on this. Structured exception handling reduces debugging and repair time
dramatically.
C# Coding Standards and Best Programming Practices
Suresh Dasari Apr 26, 2010
Anybody can write code. With a few months of programming experience, you can write 'working applications'. Making it work is easy, but doing it the right way requires more work, than just making it work.
Believe it, 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 developers are 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 your code is not
reliable and maintainable, you (and your company) will be spending lot of time
to identify issues, trying to understand code etc throughout the life of your
application.
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:
1.
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.
2.
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.
3.
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”!)
|
Note :
The
terms Pascal Casing and Camel Casing are used throughout this document.
Pascal
Casing - First character of all words are Upper Case and other
characters are lower case.
Example: BackColor
Camel
Casing - First character of all words, except
the first word are Upper
Case and other characters are lower case.
Example: backColor
|
1. Use
Pascal casing for Class names
public class HelloWorld
{
...
}
2. Use
Pascal casing for Method names
void SayHello(string
name)
{
...
}
3. Use
Camel casing for variables and method parameters
int totalCount = 0;
void SayHello(string name)
{
string fullMessage = "Hello " + name;
...
}
4. Use
the prefix “I” with Camel Casing for interfaces ( Example: IEntity )
5. Do
not use Hungarian notation to name variables.
In earlier days most of the programmers liked it - having the data
type as a prefix for the variable name and using m_ as prefix for member
variables. Eg:
string m_sName;
int nAge;
However, in .NET coding standards, this is not recommended. Usage
of data type and m_ to represent member variables should not be used. All
variables should use camel casing.
|
Some
programmers still prefer to use the prefix m_ to represent member variables, since
there is no other easy way to identify a member variable.
|
6. Use
Meaningful, descriptive words to name variables. Do not use abbreviations.
Good:
string address
int salary
Not Good:
string nam
string addr
int sal
7. Do
not use single character variable names like i, n, s etc.
Use names like index, temp
One exception in this case would be variables used for iterations
in loops:
for ( int i = 0; i < count; i++ )
{
...
}
If the variable is used only as a counter for iteration and is not
used anywhere else in the loop, many people still like to use a single char
variable (i) instead of inventing a different suitable name.
8. Do
not use underscores (_) for local variable names.
9. All
member variables must be prefixed with underscore (_) so that they can be
identified from other local variables.
10. Do not use
variable names that resemble keywords.
11. Prefix boolean variables, properties and
methods with “is”
or similar prefixes.
Ex: private bool _isFinished
12. Namespace
names should follow the standard pattern
...
13. 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.
a. 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 intelligence.
b. 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
|
14. File name
should match with class name.
For example, for the class HelloWorld, the file name should be
helloworld.cs (or, helloworld.vb)
15. Use Pascal
Case for file names.
1. Use
TAB for indentation. Do not use SPACES. Define the Tab size as 4.
2. 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 );
3. Curly
braces ( {} ) should be in the same level
as the code outside the braces.
4. 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;
}
5. There
should be one and only one single blank line between each method inside the
class.
6. 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
}
7. 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++)
{
//
}
}
8. 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.
9. Keep
private member variables, properties and methods in the top of the file and
public members in the bottom.
1. Avoid
writing very long methods. A method should typically have 1~25 lines of code.
If a method has more than 25 lines of code, you must consider re factoring into
separate methods.
2. Method
name should tell what it does. Do not use mis-leading names. If the method name
is obvious, there is no need of documentation explaining what the method does.
Good:
void SavePhoneNumber ( string phoneNumber )
{
// Save the phone number.
}
Not Good:
// This method will save the phone number.
void SaveDetails ( string phoneNumber )
{
// Save the phone number.
}
3. A
method should do only 'one job'. Do not combine more than one job in a single
method, even if those jobs are very small.
Good:
// Save the address.
SaveAddress ( address );
// Send an email to the supervisor to inform that the address is updated.
SendEmail ( address, email
);
void SaveAddress ( string address )
{
// Save the address.
// ...
}
void SendEmail ( string address, string email )
{
// Send an email to inform the supervisor that the address is changed.
// ...
}
Not Good:
// Save address and send an email to the supervisor to inform that
// the address is updated.
SaveAddress ( address, email );
void SaveAddress ( string address, string email )
{
// Job 1.
// Save the address.
// ...
// Job 2.
// Send an email to inform the supervisor that the address is changed.
// ...
}
4. Use
the c# or VB.NET specific types (aliases), rather than the types defined in
System namespace.
int age; (not Int16)
string name; (not String)
object contactInfo; (not Object)
|
Some developers prefer to use
types in Common Type System than language specific aliases.
|
5. Always
watch for unexpected values. For example, if you are using a parameter with 2
possible values, never assume that if one is not matching then the only
possibility is the other value.
Good:
If ( memberType == eMemberTypes.Registered )
{
// Registered user… do something…
}
else if ( memberType == eMemberTypes.Guest )
{
// Guest user... do something…
}
else
{
// Un
expected user type. Throw an exception
throw new
Exception (“Un expected value “ + memberType.ToString() + “’.”)
// If we
introduce a new user type in future, we can easily find
// the problem here.
}
Not Good:
If ( memberType == eMemberTypes.Registered )
{
//
Registered user… do something…
}
else
{
// Guest
user... do something…
// If we introduce another user type in future, this code will
// fail and will not be noticed.
}
6. Do
not hardcode numbers. Use constants instead. Declare constant in the top of the
file and use it in your code.
However, using constants are also not recommended. You should use
the constants in the config file or database so that you can change it later.
Declare them as constants only if you are sure this value will never need to be
changed.
7. Do
not hardcode strings. Use resource files.
8. Convert
strings to lowercase or upper case before comparing. This will ensure the
string will match even if the string being compared has a different case.
if ( name.ToLower() == “john” )
{
//…
}
9. Use
String.Empty instead of “”
Good:
If ( name == String.Empty )
{
// do something
}
Not Good:
If ( name == “” )
{
// do something
}
10. Avoid
using member variables. Declare local variables wherever necessary and pass it
to other methods instead of sharing a member variable between methods. If you
share a member variable between methods, it will be difficult to track which
method changed the value and when.
11. Use enum wherever required. Do not use
numbers or strings to indicate discrete values.
Good:
enum MailType
{
Html,
PlainText,
Attachment
}
void SendMail (string message, MailType mailType)
{
switch ( mailType )
{
case MailType.Html:
// Do something
break;
case MailType.PlainText:
// Do something
break;
case MailType.Attachment:
// Do something
break;
default:
// Do something
break;
}
}
Not Good:
void SendMail (string message, string mailType)
{
switch ( mailType )
{
case "Html":
// Do something
break;
case "PlainText":
// Do something
break;
case "Attachment":
// Do something
break;
default:
// Do something
break;
}
}
12. Do not
make the member variables public or protected. Keep them private and expose
public/protected Properties.
13. The event
handler should not contain the code to perform the required action. Rather call
another method from the event handler.
14. Do not
programmatically click a button to execute the same action you have written in
the button click event. Rather, call the same method which is called by the
button click event handler.
15. Never
hardcode a path or drive name in code. Get the application path
programmatically and use relative path.
16. Never
assume that your code will run from drive "C:". You may never know,
some users may run it from network or from a "Z:".
17. In the
application start up, do some kind of "self check" and ensure all
required files and dependancies are available in the expected locations. Check
for database connection in start up, if required. Give a friendly message to
the user in case of any problems.
18. If the
required configuration file is not found, application should be able to create
one with default values.
19. If a wrong
value found in the configuration file, application should throw an error or
give a message and also should tell the user what are the correct values.
20. Error
messages should help the user to solve the problem. Never give error messages
like "Error in Application", "There is an error" etc.
Instead give specific messages like "Failed to update database. Please
make sure the login id and password are correct."
21. When displaying
error messages, in addition to telling what is wrong, the message should also
tell what should the user do to solve the problem. Instead of message like
"Failed to update database.", suggest what should the user do:
"Failed to update database. Please make sure the login id and password are
correct."
22. Show short
and friendly message to the user. But log the actual error with all possible
information. This will help a lot in diagnosing problems.
23. Do not
have more than one class in a single file.
24. Have your
own templates for each of the file types in Visual Studio. You can include your
company name, copy right information etc in the template. You can view or edit
the Visual Studio file templates in the folder C:\Program
Files\Microsoft Visual Studio 8\Common7\IDE\ItemTemplatesCache\CSharp\1033.
(This folder has the templates for C#, but you can easily find the
corresponding folders or any other language)
25. Avoid
having very large files. If a single file has more than 1000 lines of code, it
is a good candidate for refactoring. Split them logically into two or more
classes.
26. Avoid
public methods and properties, unless they really need to be accessed from
outside the class. Use “internal” if they are accessed only within the same
assembly.
27. Avoid
passing too many parameters to a method. If you have more than 4~5 parameters,
it is a good candidate to define a class or structure.
28. If you
have a method returning a collection, return an empty collection instead of
null, if you have no data to return. For example, if you have a method
returning an ArrayList, always return a valid ArrayList. If you have no items
to return, then return a valid ArrayList with 0 items. This will make it easy
for the calling application to just check for the “count” rather than doing an
additional check for “null”.
29. Use the
AssemblyInfo file to fill information like version number, description, company
name, copyright notice etc.
30. Logically
organize all your files within appropriate folders. Use 2 level folder
hierarchies. You can have up to 10 folders in the root folder and each folder
can have up to 5 sub folders. If you have too many folders than cannot be
accommodated with the above mentioned 2 level hierarchy, you may need re
factoring into multiple assemblies.
16. Make sure
you have a good logging class which can be configured to log errors, warning or
traces. If you configure to log errors, it should only log errors. But if you
configure to log traces, it should record all (errors, warnings and trace).
Your log class should be written such a way that in future you can change it
easily to log to Windows Event Log, SQL Server, or Email to administrator or to
a File etc without any change in any other part of the application. Use the log
class extensively throughout the code to record errors, warning and even trace
messages that can help you trouble shoot a problem.
17. If you are
opening database connections, sockets, file stream etc, always close them in
the finally block.
This will ensure that even if an exception occurs after opening the connection,
it will be safely closed in the finally block.
18. Declare
variables as close as possible to where it is first used. Use one variable
declaration per line.
19. Use
StringBuilder class instead of String when you have to manipulate string
objects in a loop. The String object works in weird way in .NET. Each time you
append a string, it is actually discarding the old string object and recreating
a new object, which is a relatively expensive operations.
Consider the following example:
public string ComposeMessage (string[] lines)
{
string message = String.Empty;
for (int i = 0; i < lines.Length; i++)
{
message += lines [i];
}
return message;
}
In the above example, it may look like we are just appending to
the string object ‘message’. But what is happening in reality is, the string
object is discarded in each iteration and recreated and appending the line to
it.
If your loop has several iterations, then it is a good idea to use
StringBuilder class instead of String object.
See the example where the String object is replaced with
StringBuilder.
public string ComposeMessage (string[] lines)
{
StringBuilder message = new
StringBuilder();
for (int i = 0; i < lines.Length; i++)
{
message.Append( lines[i]
);
}
return message.ToString();
}
1. Always
use multi layer (N-Tier) architecture.
2. 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.
3. 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.
4. 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.
1. 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 usingSystem.Web.HttpCOntext.Current.Session
2. Do
not store large objects in session. Storing large objects in session may
consume lot of server memory depending on the number of users.
3. 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
Good and meaningful comments make code more maintainable. However,
1. Do
not write comments for every line of code and every variable declared.
2. Use // or /// for
comments. Avoid using /* … */
3. Write
comments wherever required. But good readable code will require very less
comments. If all variables and method names are meaningful, that would make the
code very readable and will not need many comments.
4. Do
not write comments if the code is easily understandable without comment. The
drawback of having lot of comments is, if you change the code and forget to
change the comment, it will lead to more confusion.
5. Fewer
lines of comments will make the code more elegant. But if the code is not
clean/readable and there are less comments, that is worse.
6. If
you have to use some complex or weird logic for any reason, document it very
well with sufficient comments.
7. If
you initialize a numeric variable to a special number other than 0, -1 etc,
document the reason for choosing that value.
8. The
bottom line is, write clean, readable code such a way that it doesn't need any
comments to understand.
9. Perform
spelling check on comments and also make sure proper grammar and punctuation is
used.
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
Common .NET Naming Conventions
These
are the industry-accepted standard naming conventions for C# and VB.NET
programs. For additional information, please see the MSDN help documentation
and the book referenced above. While individual naming conventions at
organizations may vary (Microsoft only suggests conventions for public and
protected items), the list below is quickly becoming the de-facto standard in
the industry. Please note the absence of Hungarian Notation. These naming
standards should find their way into all of your .NET development, including
ASP.NET Web applications, WPF, Silverlight and Windows Forms applications.
Note
that while this document predates the online and printed standards
documentation from Microsoft, everything below which indicates it is based on
.NET library standards is consistent with that documentation and Brad's book.
In areas where Microsoft has not provided guidance (Microsoft generally doesn't
care what you do in private/non-exposed code. In fact, they aren't even
consistent in their internal code in the .NET framework), de facto standards
have emerged, and I have captured them here.
The
"ux" naming convention for controls is something I have added and
found to be helpful in Windows Forms, but not so much in Silverlight and WPF.
It is not based on any official standards, but instead based upon a multitude
of projects by my teams and others, as well as on-line discussions on the
topic. While I strongly recommend that you follow Microsoft guidelines when
present, I encourage you to try out the items marked as extensions below and
see how they work for you before committing to them.
|
*
Public class-level variables are universally frowned upon. It is considered to
be a much better practice to use property procedures (accessors and mutators)
to provide read and/or write access to a private member variable. If you must
expose a member variable to other classes using "Public", follow the
property naming conventions, but don't complain if your guilty conscience keeps
you up at night ;-).
what is this code?
ReplyDeleteklinik Raphael