Coding Standards for .Net
C# Coding Standards
The purpose of creating this set of rules is
an attempt to set standards for C# coding that would be convenient and practical
at the same time. Not all of these rules have definitive ground. We simply
accepted some of them as standards. After all, it does not matter what you
choose, but rather how strictly you follow the selected rules.
Basic
Principles
This document cannot cover
all the cases you may face. If you have doubts, rely on the basic principles,
applicable in any situation. Some of these principles are:
- The Principle
of Least Surprise (POLA).
Always favor the most obvious solution, to make your code
understandable and not to confuse other developers
- Keep It Simple
Stupid (KISS).
Choose the simplest solution for your tasks
- You Ain’t Gonna
Need It (YAGNI).
Work on the task at hand, do not create code only because you think you
are going to need it in the future
- Don't Repeat
Yourself (DRY).
Abstain from duplicating code within one component, repository and keep in
mind the rule of three.
- The four
principles of object-oriented programming: encapsulation, abstraction,
inheritance, and polymorphism.
- Generally, you
do not have to make the entire code base compliant with these
guidelines. Nevertheless, it should comply as much as possible.
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 you may follow any of
them. What is more important is, selecting one standard approach and ensuring
that everyone is following it.
Acknowledgement
This document is based on the coding style
that is prevalent in Microsoft Developer Network
(MSDN) example code, and should already be
familiar to most developers. The guidelines presented here were not created in
a vacuum. In the process of creating this document, the authors have scanned
many existing .NET code conventions and guideline documents including
How to follow the standards across the team
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 discuss the
standards mentioned in this document or develop 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.
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.
Draft a new version of this document with appropriate changes based on the
suggestions from all of the team members.
Naming conventions make programs more
understandable by making them easier to read and ensuring consistency.
Choosing identifiers that conform to these
guidelines improves the reusability of your code.
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 |
Below are our C# coding standards,
naming conventions, and best practices. Use these in your own projects and/or
adjust these to your own needs.
General Naming
Conventions
Choose easily readable identifier names, and
favor readability over brevity. The property name
CanScrollHorizontally is
better than ScrollableX
(an obscure reference to the X-axis).
Choose semantically meaningful names rather
than language-specific keywords for type names.
(GetLength
is more meaningful than GetInt.)
Avoid using underscores, hyphens, or any
other non-alphanumeric characters. When an identifier consists of multiple words,
do not use separators, such as underscores ("_") or hyphens
("-"), between words. Instead, use casing to indicate the beginning
of each word.
Avoid abbreviations or contractions (e.g.
use OnButtonClick rather
than OnBtnClick)
Do not use any acronyms that are not widely
accepted, and then only when necessary.
Avoid using identifiers that conflict with
keywords of widely used programming languages.
Though most keywords can be made to work as
regular identifiers, doing so is confusing to read.
Names of
Assemblies and DLLs
An assembly contains all or part of a
reusable library and is contained in a single dynamic-link library (DLL).
Assemblies and DLLs are the physical organization of a library (namespaces are
a logical organization and should be factored independent of the assembly's
organization).
Choose names for your assembly DLLs that
suggest large chunks of functionality such as System.Data. Assembly and DLL names do not have to
correspond to namespace names but it is reasonable to follow the namespace name
when naming assemblies.
Name DLLs according to the pattern:
<Product Name>.<Functionality>[.<Component>].dll
For example: SampleProject.Configuration.DataAccess.dll
Additional levels may be added to
<Functionality> or
<Component> to
subdivide large collections of functionality.
Names of
Namespaces
The name chosen for a namespace should
indicate the functionality made available by types in the namespace.
Use a stable, version-independent product
name at the second level of a namespace name.
Use Pascal casing, and separate namespace
components with periods.
Do not use generic type names such as
Element, Node, Log, or Message, or names in
the functionality or component namespaces. There is a very high probability
these will cause type name conflicts in common scenarios.
doorganize namespaces with a clearly
defined structure
1.
// Examples
2.
namespace Company.Product.Module.SubModule
3.
namespace Product.Module.Component
4.
namespace Product.Layer.Module.Group
Names Of Class
Names
Do use PascalCasing for
class names.
1.
public class ClientActivity
2.
{
3.
public void ClearStatistics()
4.
{
5.
//...
6.
}
7.
public void CalculateStatistics()
8.
{
9.
//...
10. }
11. }
Noun Class Names
douse noun or noun phrases to name a
class.
1.
public class Employee
2.
{
3.
}
4.
public class BusinessLocation
5.
{
6.
}
7.
public class DocumentCollection
8.
{
9.
}
Names Of Interfaces
Do prefix interfaces with the letter I. Interface
names are noun (phrases) or adjectives.
1.
public interface IShape
2.
{
3.
}
4.
public interface IShapeCollection
5.
{
6.
}
7.
public interface IGroupable
8.
{
9.
}
Names
Of Variable Names
douse camelCasing for local
variables and method arguments.
1.
public class UserLog
2.
{
3.
public void Add(LogEvent
logEvent)
4.
{
5.
int
itemCount = logEvent.Items.Count;
6.
// ...
7.
}
8.
}
Names
Of Identifiers
do notuse Hungarian notation or
any other type identification in identifiers
1.
// Correct
2.
int counter;
3.
string name;
4.
5.
// Avoid
6.
int iCounter;
7.
string
strName;
Names Of Constants
do notuse Screaming Caps for
constants or readonly variables
1.
// Correct
2.
public static const string ShippingType = "DropShip";
3.
4.
// Avoid
5.
public static const string
SHIPPINGTYPE = "DropShip";
Names Of Abbreviations
avoidusing Abbreviations.
Exceptions: abbreviations commonly used as names, such as Id, Xml, Ftp,
Uri
1.
// Correct
2.
UserGroup
userGroup;
3.
Assignment
employeeAssignment;
4.
5.
// Avoid
6.
UserGroup usrGrp;
7.
Assignment empAssignment;
8.
9.
// Exceptions
10. CustomerId
customerId;
11. XmlDocument
xmlDocument;
12. FtpHelper
ftpHelper;
13. UriPart
uriPart;
Abbreviation Casing
douse PascalCasing for
abbreviations 3 characters or more (2 chars are both uppercase)
1.
HtmlHelper
htmlHelper;
2.
FtpTransfer
ftpTransfer;
3.
UIControl
uiControl;
Names Of Type Names
douse predefined type
names instead of system type names like Int16, Single, UInt64, etc
1.
// Correct
2.
string
firstName;
3.
int lastIndex;
4.
bool isSaved;
5.
6.
// Avoid
7.
String
firstName;
8.
Int32 lastIndex;
9.
Boolean
isSaved;
Implicit Types
douse implicit type var for
local variable declarations.
Exception: primitive types (int, string, double, etc) use predefined names.
1.
var stream = File.Create(path);
2.
var customers = new Dictionary<int?,
customer="" style="box-sizing: border-box;">();
3.
<int?, customer="" style="box-sizing:
border-box;">
4.
<int?, customer=""
style="box-sizing: border-box;">// Exceptions
5.
<int?, customer=""
style="box-sizing: border-box;">int index =
100;
6.
<int?, customer=""
style="box-sizing: border-box;">string
timeSheet;
7.
<int?, customer=""
style="box-sizing: border-box;">bool
isCompleted;
Names Of File Names
doname source files according to their
main classes.
Exception: file names with partial
classes reflect their source or purpose, e.g. designer, generated, etc.
1.
// Located in Task.cs
2.
public partial class Task
3.
{
4.
//...
5.
}
1.
// Located in Task.generated.cs
2.
public partial class Task
3.
{
4.
//...
5.
}
Names Of Member Variables
dodeclare all member variables at the
top of a class, with static variables at the very top.
1.
// Correct
2.
public class Account
3.
{
4.
public static string BankName;
5.
public static decimal Reserves;
6.
7.
public string Number {get; set;}
8.
public DateTime DateOpened {get; set;}
9.
public DateTime DateClosed {get; set;}
10. public decimal Balance {get; set;}
11.
12. // Constructor
13. public Account()
14. {
15. // ...
16. }
17. }
Names Of Enums
douse singular names for enums.
Exception: bit field enums.
1.
// Correct
2.
public enum Color
3.
{
4.
Red,
5.
Green,
6.
Blue,
7.
Yellow,
8.
Magenta,
9.
Cyan
10. }
11.
12. //
Exception
13. [Flags]
14. public enum Dockings
15. {
16. None = 0,
17. Top = 1,
18. Right = 2,
19. Bottom = 4,
20. Left = 8
21. }
Enum Types
do notexplicitly specify a type of an enum
or values of enums (except bit fields)
1.
// Don't
2.
public enum Direction : long
3.
{
4.
North = 1,
5.
East = 2,
6.
South = 3,
7.
West = 4
8.
}
9.
10. //
Correct
11. public enum Direction
12. {
13. North,
14. East,
15. South,
16. West
17. }
Enum Suffix
do notsuffix enum names with Enum
1.
// Don't
2.
public enum CoinEnum
3.
{
4.
Penny,
5.
Nickel,
6.
Dime,
7.
Quarter,
8.
Dollar
9.
}
10.
11. //
Correct
12. public enum Coin
13. {
14. Penny,
15. Nickel,
16. Dime,
17. Quarter,
18. Dollar
19. }
Others
No
Underscores
do notuse Underscores in
identifiers.
Exception: you can prefix private static variables with an underscore.
1.
// Correct
2.
public DateTime
clientAppointment;
3.
public TimeSpan
timeLeft;
4.
5.
// Avoid
6.
public DateTime client_Appointment;
7.
public TimeSpan
time_Left;
8.
9.
// Exception
10. private DateTime
_registrationDate;
Curly
Brackets
Doverti
cally align curly brackets.
1.
// Correct
2.
class Program
3.
{
4.
static void Main(string[] args)
5.
{
6.
}
7.
}
Test
naming is important for teams on long term project as any other code style conventions.
By applying code convention in tests you proclaim that each test name will be
readable, understandable and will have a well-known naming pattern for everyone
on the project.
There are few recommendations regarding
test naming:
·
Test name should express a specific requirement
·
Test name could include the expected input or state and the expected
result for that input or state
·
Test name should
be presented as a statement or fact of life that expresses workflows and
outputs
·
Test name could include
the name of the tested method or class
MethodName_ExpectedBehavior_StateUnderTest
For
Example:
Get_ShouldReturnListOfCustomers_WhenQueryParametersAreValid()
Get_ShouldReturnBadRequest_WhenRequestIsInvalid()
Post_ShouldInsertNewCustomer_WhenDataIsValid()
Post_ShouldThrowException_WhenCustomerTypeIsInvalid()
Both General Comments and XML Documentation
Comments are encouraged in VA code. General comments are comments which are
delimited by /* and */, or //. XML Documentation Comments are delimited with
///.
General Comments
General comments are meant to aid developers
in further understanding code and implementation decisions. General comments
should contain only information that is relevant to reading and understanding
the program. Discussion of nontrivial or unobvious design decisions is
appropriate, but avoid duplicating information that is present in (and clear
from) the code.
In general, avoid any comments that are
likely to get out of date as the code evolves.
Temporary comments that are expected to be
changed, or removed later, should be marked with special tokens so that they
can easily be found.
Ideally, all temporary comments shall be
removed by the time a program is ready to be moved to production.
Comments should not be enclosed in large
boxes drawn with asterisks or other characters and should not include special
characters such as form-feed and backspace.
Single-Line Comments
Short comments can appear on a single line
indented to the level of the code that follows. If a comment can't be written
in a single line, it should follow the block comment format. A single-line
comment should be preceded by a blank line.
Here's an example of a single-line comment
in .NET
1.
if (bar
> 1) {
2.
bar--;
3.
//Do a triple-flip
4.
}
The // comment delimiter should not be used
on consecutive full lines for text comments. However, it can be used in
consecutive multiple lines for commenting out sections of code.
1. //if (bar > 1) {
2.
// bar--;
3.
//
4.
// // Do a triple-flip.
5.
// ...
6.
//}
Trailing
Comments
Trailing comments are very
short comments that appear on the same line as the code they describe. Trailing
comments should be shifted far enough to the right in order to separate them
from the statements. Multiple trailing comments contained in a section of code
should be indented to the same tab setting.
The // comment delimiter
can comment out a complete line or only a partial line.
1.
if (foo > 1) {
2.
foo--;
3.
// Do a double-flip.
4.
...
5.
}
6.
else {
7.
return false; // foo <=1, no double-flip
8.
}
Block
Comments
Block comments are used to
provide descriptions of files, methods, data structures and algorithms. Block
comments may be used at the beginning of a file and/or before a method or
class. They can also be used in other places, such as within methods. Block
comments inside a function or method should be indented to the same level as
the code they describe.
A block comment should be
preceded by a blank line. This sets it apart from the rest of the code.
For example:
/*
* Here is a block comment.
* It extends over several lines,
* and uses the proper formatting.
*/
Special
Tokens in Comments
In addition to general
comments, Microsoft Visual Studio allows developers to place special tokens in
comments to indicate areas where there is additional work to be completed or a
known issue needs to be corrected.
These types of comments
indicate that the code is not complete, or is not implemented in an optimal
manner.
1.
if (foo > 1) {
2.
// Do a double-flip.
3.
...
4.
}
5.
else {
6.
// HACK: Would be better if bar() would
accept foo value
7.
return false;
8.
}
9. // TODO: Implement triple-flip
These special token
comments can be viewed in the Task List in the IDE. The default special tokens
are TODO, HACK, and UNDONE, but custom tokens may be added
Released source code shall
not contain special token comments showing incomplete work.
XML
Documentation Comments
In Visual C#, you can
create documentation for your code by including XML elements in special comment
fields (indicated by triple slashes) in the source code directly before the
code block to which the comments refer, for example:
///
<summary>
/// This class performs an
important function.
/// </summary>
public class MyClass{}
When you compile with the
/doc option, the compiler will search for all XML tags in the source code and
create an XML documentation file. To create the final documentation based on
the compiler-generated file, you can create a custom tool or use a tool such as
Sandcastle.
XML documentation comments
can also provide valuable pop-up tips to those using libraries where methods
and classes are appropriately documented.
For usage information, see How
to: Use the XML Documentation Features in the Microsoft Developer Network C#
Programming Guide.
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. Share the file link to
all team members before the meeting. Let them read and come up with points
for discussion. 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”!)
- Live
Share - Live
Share is a feature of
Visual Studio that enables real-time collaboration between developers. It
gives users the ability to share a session with someone else, allowing
them to edit code as well as share a server and debugging session. You can
make use of this to review the code.
Comments
Post a Comment