C# Coding Guidelines And Best Practices v1.0

This document describes the rule and recommendations for developing software application and class libraries in .NET using C# as a language. The main purpose is to define the general guidelines to enforce consistent coding styles and formatting that can be useful for developers to avoid common mistakes they do while development of software applications using C#. This document covers naming conventions, coding styles and some architecture level suggestions.
 

Purpose of coding standard and best practices to do it

 
Coding standards are a set of guidelines used for programming language that recommends programming style and best practices to achieve it.
 
The coding standards generally covers indentation, comments, naming conventions, programming practices, file structure within project, architectural best practices etc. Software developers are highly recommended to follow these guidelines. The coding guidelines have following advantages.
  • Increases the readability of source code written.
  • Will contain fewer bugs and work more efficiently.
  • Requires less maintenance.
  • Easier for old and new developers to maintain and modify the code.
  • Leads to increase in productivity of developers.
  • Reduces the overall cost for software development.
  • Make the difference between a successful project and a project that is, at worst, dead on delivery.

Naming Conventions

 
There are three type of naming conventions generally used while doing C# programming,
  • Pascal Convention – First character of all word is in upper case and other characters are in lower case.
    Example: HelloWorld

  • Camel Case Convention – The first character of all words, except the first word, is upper case and other characters are lower case.
    Example: helloWorld

  • Hungarian Case Convention – The data type as prefix is used to define the variable by developers long ago. This convention is not used anywhere now a day’s except local variable declaration.

    Example:string m_sName; string strName; int iAge;
Sr.
No
Module
Description
Correct
Wrong
1.
Class
Use Pascal conventions for defining class name
public class HelloWorld
{
}
public class helloWorld
{
}
2.
Method
Use Pascal conventions
for defining class name
public void AddNumbers(int first, int second)
{
}
public void addNumbers(int first, int second)
{
}
3.
Interface
Use Prefix “I” with Camel Casing to define
interface
public interface IEmployee
{
}
public interface Iemployee
{
}
4.
Local Variables
Use Hungarian Meaningful, descriptive words to name variables. Do
not use abbreviations
string firstName int salary
string fName
string name string _firstName int sal
5.
Member Variables
Member variables must be prefix with underscore (_
) so that they can be identified by other local variables and
constants
private IEmployee _employeeService = null
private UserRole userRole
private UserGroup userGroup
private IEmployee empService
= null
private UserRole usrRole private UserGroup usrGrp
6.
Boolean variables
Prefix Boolean variables with
“is” or some
private bool _isAccepted
private bool _isFinished
private bool accepted
private bool finished private bool isFinished
7.
Namespa ce
The namespace should be logical grouping of classes with specific pattern
<CompanyName>
.<ProductName>
.<ModuleName>
Example: ABC.SchoolManagement.BusinessLayer ABC.SchoolManagement.DataAccessLayer ABC.SchoolManagement.WebUI
Example:
BusinessLayer DataAccessLayer
WebUI
8.
File Name
Filename should match with class name i.e.
Pascal name
public class HelloWorld
{
}
The Filename must be: HelloWorld.cs
public class HelloWorld
{
}
Filename: helloworld.cs
 

Control Prefix Naming Conventions

 
The best practice generally used while developing web and window application uses prefixes for UI elements. Since ASP.NET and Windows Form have too many controls available to use, hence summarizing them through tabular chart.
 
Sr.No
Control
Prefix
1
Label
lbl
2
TextBox
txt
3
DataGrid
grd
4
Button
btn
5
ImageButton
imb
6
Hyperlink
hyp
7
DropDownList
ddl
8
ListBox
lst
9
DataList
dtl
10
Repeater
rep
11
Checkbox
chk
12
CheckBoxList
cbl
13
RadioButton
rdo
14
RadioButtonList
rbl
15
Image
img
16
Panel
pnl
17
PlaceHolder
phd
18
Table
tbl
19
Validators
val
 

Code Indentation and Comments

 
Good layout uses formatting to emphasize the structure of code and to make the code easier to read. To achieve this the below points would be helpful,
  • Use default code editor setting provided by Microsoft Visual Studio.
  • Write only one statement and declaration per line.
  • Add one blank line space between each method.
  • Use parentheses to understand the code written.
  • Use xml commenting to describe functions, class and constructor.
  • Use Tab for indentation.
  • Use one blank line to separate logical groups of code.
  • Use #region and #endregion to group related piece of code as per below
    • Private Member
    • Private Properties
    • Public Properties
    • Constructors
    • Event Handlers/Action Methods
    • Private Methods
    • Public Methods
  • Do not write comments for every line of code and every variable declared.
  • Use // or /// for comments avoid using /* … */
  • If you have to use some complex for any reason, document it very well with sufficient comments.
  • If all variables and method names are meaningful, that would make the code very readable and will not need many comments.

Good Programming Practices

  • Avoid writing long functions. The typical function should have max 40-50 lines of code. If method has more than 50 line of code, you must consider re factoring into separate private methods.
  • Avoid writing long class files. The typical class file should contain 600-700 lines of code. If the class file has more than 700 line of code, you must create partial class. The partial class combines code into single unit after compilation.
  • Don’t have number of classes in single file. Create a separate file for each class.
  • Avoid the use of var in place of dynamic.
  • Add a whitespace around operators, like +, -, ==, etc.
  • Always succeed the keywords if, else, do, while, for and foreach, with opening and closing parentheses, even though the language does not require it.
  • The method name should have meaningful name so that it cannot mislead names. The meaningful method name doesn’t need code comments.
    1. Good: private void SaveAddress(Address address) {}  
    2. Bad:  
    3.     // This method used to save address  
    4.     private void Save(Address address) {}  
  • The method or function should have only single responsibility (one job). Don’t try to combine multiple functionalities into single function.
    1. Good: public void UpdateAddress(Address address) {}  
    2. public void InsertAddress(Address address) {}  
    3. Bad: public void SaveAddress(Address address) {  
    4.     if (address.AddressId == 0) {} else {}  
    5. }  
  • Controller Actions in MVC should have meaningful names and each action have single responsibility only.
    1. Good: public class EmployeeController: Controller {  
    2.     public ActionResult Index() {}  
    3.     public ActionResult Create() {}  
    4.         [HttpPost]  
    5.     public ActionResult Create(EmployeeModel employee) {}  
    6.     public ActionResult Edit(int id) {}  
    7.         [HttpPut]  
    8.     public ActionResult Update(EmployeeModel employee) {}  
    9.         [HttpDelete]  
    10.     public JsonResult Delete(int id) {}  
    11. }  
    12. Bad: public class EmployeeController: Controller {  
    13.     public ActionResult GetAll() {}  
    14.     public ActionResult CreateEmployee() {}  
    15.         [HttpPost]  
    16.     public ActionResult CreateEmployee(EmployeeModel employee) {}  
    17.     public ActionResult EditEmployee(int id) {}  
    18.         [HttpPut]  
    19.     public ActionResult UpdateEmployee(EmployeeModel employee) {}  
    20.         [HttpDelete]  
    21.     public JsonResult EmployeeDelete(int id) {}  
    22. }  
In above example you are talking about employee then there should no action method name with Employee prefix or extension
  • Avoid using common type system. Use the language specific aliases
    1. Good:  
    2. int age;  
    3. string firstName;  
    4. object addressInfo;  
    5. Bad:  
    6. System.Int32 age; String firstName;  
    7. Object addressInfo;  
  • Do not hardcode string or numbers; instead, create separate file sfor constants and put all constants into that or declare constants on top of file and refer these constants into your code.
  • You can also put some constants like database connection, logger file name, SMTP information variables etc. in form of key and value pair in config file.
  • Don’t hardcode strings. Use resource files.
  • While comparing string, convert string variables into Upper or Lower case
    1. Good: if (firstName.ToLower() == "yogesh") {}  
    2. if (firstName.ToUpper() == “YOGESH”) {}  
    3. Bad: if (firstName == “rohit”) {}  
  • Use String.Empty instead of “”
    1. Good: if (firstName == String.Empty) {}  
    2. Bad: if (firstName == “”) {}  
  • Use enums wherever required. Don’t use numbers or strings to indicate discrete values.
    1. Good: public enum LoggerType {  
    2.     Event,  
    3.     File,  
    4.     Database  
    5. }  
    6. public void LogException(string message, LoggerType loggerType) {  
    7.     switch (loggerType) {  
    8.         case LoggerType.Event:  
    9.             // Do something break;  
    10.         case LoggerType.File:  
    11.             // Do something break;  
    12.         case LoggerType.Database:  
    13.             // Do something break;  
    14.         default:  
    15.             // Do something break;  
    16.     }  
    17. }  
    18. Bad: public void LogException(string message, LoggerType loggerType) {  
    19.     switch (loggerType) {  
    20.         case "Event":  
    21.             // Do something break;  
    22.         case "File":  
    23.             // Do something break;  
    24.         case "Database":  
    25.             // Do something break;  
    26.         default:  
    27.             // Do something break;  
    28.     }  
    29. }  
  • The event handler should not contain the code to perform the required action. Instead call another private or public method from the event handler. Keep event handler or action method as clean as possible.
  • Never hardcode a path or drive name in code. Get the application path programmatically and use relative path. Use input or output classes System.IO) to achieve this.
  • Always do null check for objects and complex objects before accessing them.
    1. Good: public Contact GetContactDetails(Address address) {  
    2.     if (address != null && address.Contact != null) {  
    3.         return address.Contact;  
    4.     }  
    5. }  
    6. Bad: public Contact GetContactDetails(Address address) {  
    7.     return address.Contact;  
    8. }  
  • Error message to end use should be user friendly and self-explanatory but log the actual exception details using logger. Create constants for this and use them in application.
    1. Good:  
    2. “Error occurred while connecting to database. Please contact administrator.” “Your session has been expired. Please login again.”  
    3. Bad:  
    4. “Error in Application.”  
    5. “There is an error in application.”  
  • Avoid public methods and properties to expose, unless they really need to be accessed from outside the class. Use internal if they are accessed only within the same assembly and use private if used in same class.
  • Avoid passing many parameters to function. If you have more than 4-5 parameters use class or structure to pass it.
    1. Good: public void UpdateAddress(Address address) {}  
    2. Bad: public void UpdateAddress(int addressId, string country, string state, string phoneNumber, string pinCode, string address1, string address2) {}  
  • While working with collection be aware of the below points,
    • While returning collection return empty collection instead of returning null when you have no data to return.
    • Always check Any() operator instead of checking count i.e. collection.Count > 0 and checking of null
    • Use foreach instead of for loop while traversing.
    • Use IList<T>, IEnumrable<T>,ICollection<T> instead of concrete classes e.g. using List<>
  • Use object initializers to simplify object creation.
    1. Good: var employee = new Employee {  
    2.     FirstName = “ABC”, LastName = “PQR”, Manager = “XYZ”, Salary = 12346.25  
    3. };  
    4. Bad: var employee = new Employee();  
    5. employee.FirstName = “ABC”;  
    6. employee.LastName = “PQR”;  
    7. employee.Manager = “XYZ”;  
    8. employee.Salary = 12346.25;  
  • The using statements should be sort by framework namespaces first and then application namespaces in ascending order
    1. using System;  
    2. using System.Collections.Generic; using System.IO;  
    3. using System.Text;  
    4. using Company.Product.BusinessLayer;  
  • 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.
  • Simplify your code by using the C# using statement. If you have a try-finally statement in which the only code in the finally block is a call to the Dispose method, use a using statement instead.
    1. Good: using(var fileToOpen = new FileInfo(fileName)) {  
    2.     // File operation  
    3. }  
    4. Bad: var fileInfo = new FileInfo(fileName);  
    5. try {  
    6.     // File operation  
    7. finally {  
    8.     if (fileInfo != null) {  
    9.         fileInfo.Delete();  
    10.     }  
    11. }  
  • Always catch only the specific exception instead of catching generic exception.
    1. void ReadFile(string fileName) {  
    2.     try {  
    3.         // read from file.  
    4.     } catch (System.IO.IOException fileException) {  
    5.         // log the error. Re-throw exception throw fileException;  
    6.     } finally {}  
    7. }  
    8. Bad: void ReadFile(string fileName) {  
    9.     try {  
    10.         // read from file.  
    11.     } catch (Exception ex) {  
    12.         // catching general exception  
    13.     } finally {}  
    14. }  
  • Use StringBuilder class instead of String when you have to manipulate string objects in a loop. The String object works in a 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 operation.

Architecture Design Level Guidelines

  • Always use multi tier (N-Tier) Architecture.
  • Implement loosely coupled architecture using interfaces and abstract class.
  • Use of generics would help you to make reusable classes and functions.
    1. public class MyClass < T > where T: SomeOtherClass {  
    2.     public void SomeMethod(T t) {  
    3.         SomeOtherClass obj = t;  
    4.     }  
    5. }  
  • Separate your application into multiple assemblies. Create separate assemblies for UI, Business Layer, Data Access Layer, Framework, Exception handling and Logging components.
  • Do not access database from UI pages. Use data access layer to perform all tasks which are related to database.
  • Always use stored procedure instead of writing inline queries in C# code.
  • Always use transaction in database operation like Create, Update, and Delete. This would be helpful to roll back old data again in case of any exception occurred while execution of Sql statement.
  • Don’t put complex logic inside stored procedure instead of it put it into the business layer.
  • Don’t use prefix as “sp” or “sp_” to the user defined stored procedure similarly don’t use prefix as “fn” or “fn_” as the all-system level procedure start with “sp” and functions start with “fn” which triggers overload for search of procedures.
  • For installing database on client machine user installer sql scripts.
  • Try to use design pattern, practices and SOLID principles.
  • For same code, create separate utility file or move it to base class.
  • Use try-catch-finally 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 to caller layer so that the application can catch it and show the user specific message on UI
  • Don’t store large objects into Session. Storing large or complex object into session may consume server memory. Destroy or Dispose such session variable after use.
  • Don’t store large object into view state, this will increase the page load time.
  • Always refer third party dll, javascripts and css framework through NuGet package so that you can update with latest version whenever required.
  • Always refer minified version of javascript or css files, this will reduce unnecessary overhead to the server.