C# Coding Conventions, Coding Standards & Best Practices

C# Coding Conventions, Coding Standards & Best Practices

Cui, Chikun

Overview

Introduction This coding guidelines document aims to provide a common standard and practices within the organization. This guidelines will help: · Promote code readability · Promote code maintainability · Faster enablement of new resources joining the project · Mitigate bugs The content of this document comes from multiple experiences and different sources. References are noted at the end of this material. In some cases, the statements were copied verbatim from the external sources to preserve the original intent of the author; thus credit is given to the original authors. Developers are encouraged to have a clear, concise, and self-documenting code with the idea in mind that other developers may read and update it.

Description

CIO China Rapid - Coding Standards and Best Practices C#

Terminology

All through-out of this document, the following set of terminologies will help you understand the coding guidelines.

Wording Intent Justification
Do... This standard or practice should be followed in all cases. If you think that your specific application is exempt, it probably isn't. These standards are present to mitigate bugs.
Do Not... This standard or practice should never be applied. Same as above.
Consider This standard or practice should be followed in most cases. These standards are typically stylistic and attempt to promote a consistent and clear style.
Avoid This standard or practice should not be followed, unless there's reasonable justification. Same as above.
You can… This standard or practice can be followed if you want to; it's not necessarily good or bad. There are probably implications to following the practice (dependencies, or constraints) that should be considered before adopting it. These standards are typically stylistic, but are not ubiquitously adopted.

General

File and Structure

  • Do name the source file with the name of the public type it contains

Filename should follow the name of the public type. For example, if the public class is Container it should follow that the filename is Container.cs.

  • Do Not have more than one public type in a source file

Each source file should only have one public type. The exception is when the type differs only on the number of generic parameters or when one is nested in the other. Multiple internal types are allowed in the same source file.

Assembly Properties

The assembly should contain the appropriate property values describing its name, copyright, and so on.

Standard Example
Set Copyright to Copyright © [assembly: AssemblyCopyright("Copyright © Accenture Inc.  2015")]
Set AssemblyCompany to [assembly: AssemblyCompany("Accenture Incorporated")]
Set both AssemblyTitle and AssemblyProduct to the current sample name [assembly: AssemblyTitle("CSNamedPipeClient")
[assembly: AssemblyProduct("CSNamedPipeClient")]

Style and Formatting

  • Consider limiting the length of line of codes

By limiting the length of line of codes improves code readability. The maximum line of codes should be set to 130 characters. Break the codes when the line length exceeds 130 characters.

Assemblies/Libraries

  • Do reference assemblies/libraries in the project which are actually used

Make sure to only reference assemblies that are being used by the project. In some instances during development, some assemblies/libraries are used but later on new assembly version has become available. Make it a practice to remove unused assemblies/libraries.

Global Variables

  • Do minimize the use of global variables

Global variables should be passed as parameters to functions. When a global variable needs to be modified, use it either as an output parameter or return a copy of the global variable.

  • Do Not reference global variables inside a function or class

Global variables referenced inside a function or a class alters the state of the global variable without the caller knowing.

Method Declarations and Calls

  • Do follow a specific format when declaring and calling methods

The method name, return type and parameter list can take in different forms. When method declaration does not fit in a single line, break the parameter list into several lines. Both types and parameter name should be on the same line and should be aligned under the preceding one. In a similar manner, method calls should follow the same format.

  • Method Declarations
//Single line method declaration
int DoSomeProcessing(string param1, string param2, string param3); 
// Multiple line method declaration
int DoSomeProcessing(string param1, string param2, string param3,    
                              int param4, int param5, int param6);
  • Method Calls
var result = DoSomeProcessing("param1", "param2", "param3");
var result = DoSomeProcessing("param1", "param2", "param3",
                                          4, 5, 6);
  • Do declare parameters in certain order

When declaring parameters, all in parameters should come first while out parameters comes last.var result = Method(string a, byte b, out int c, out string d)

Whitespace

Blank Lines

  • Consider using blank line to separate logical group of codes
if ( ... )
{
    // Do something
    // ... 
    return false;
}    

return true;
  • Consider using one and only one single blank line between each method inside the class.
void method1 ( string parameter1 )
{   
    // Do something
}    void method2 ( string parameter1)
{
    // Do something
}

Spaces

Spaces improve readability by decreasing code density. Here are some guidelines for the use of space characters within code:

Space Guideline Example
No space between method name and parenthesis CreateFoo();
Single space after a comma Method(myChar, 0, 1);
No spaces inside bracket sx = array[index];
Single space before flow control statements while (x == y)
Single space separates operators if (x == y)

Braces

  • Do place curly braces on a separate line and not in the same line as if, for etc
if (nameProp == "Computer1")   
{       
    //Do something    
}
  • Do place curly braces in the same level as the code outside the braces
 protected int GetAge (string userName)    
 {
    //Do something here        
    if (userName == "WhosThis")        
    {            
        //Do something here        
    }   
}

Coding Conventions

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.
  • 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.

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.

dofactory Naming Conventions

do use PascalCasing for class names and method names.

public class ClientActivity{
    public void ClearStatistics()
    {
        //...
    }
    public void CalculateStatistics()
    {
        //...
    }
}

do use camelCasing for method arguments and local variables.

public class UserLog{
    public void Add(LogEvent logEvent)
    {
        int itemCount = logEvent.Items.Count;
        // ...
    }
}

do not use Hungarian notation or any other type identification in identifiers

// Correct
int counter;
string name;
// Avoid
int iCounter;
string strName;

do not use Screaming Caps for constants or readonly variables

// Correct
public static const string ShippingType = "DropShip";
// Avoid
public static const string SHIPPINGTYPE = "DropShip";

avoid using Abbreviations. Exceptions: abbreviations commonly used as names, such as Id, Xml, Ftp, Uri

// Correct
UserGroup userGroup;
Assignment employeeAssignment;
// Avoid
UserGroup usrGrp;
Assignment empAssignment;
// Exceptions
CustomerId customerId;
XmlDocument xmlDocument;
FtpHelper ftpHelper;
UriPart uriPart;

do use PascalCasing for abbreviations 3 characters or more (2 chars are both uppercase)

HtmlHelper htmlHelper;
FtpTransfer ftpTransfer;
UIControl uiControl;

do not use Underscores in identifiers. Exception: you can prefix private static variables with an underscore.

// Correct
public DateTime clientAppointment;
public TimeSpan timeLeft;
// Avoid
public DateTime client_Appointment;
public TimeSpan time_Left;
// Exception
private DateTime _registrationDate;

do use predefined type names instead of system type names like Int16, Single, UInt64, etc

// Correct
string firstName;
int lastIndex;
bool isSaved;
// Avoid
String firstName;
Int32 lastIndex;
Boolean isSaved;

do use implicit type var for local variable declarations. Exception: primitive types (int, string, double, etc) use predefined names.

var stream = File.Create(path);
var customers = new Dictionary();
// Exceptions
int index = 100;
string timeSheet;
bool isCompleted;

do use noun or noun phrases to name a class.

public class Employee
{
}
public class BusinessLocation
{
}
public class DocumentCollection
{
}

do prefix interfaces with the letter I.  Interface names are noun (phrases) or adjectives.

public interface IShape
{
}
public interface IShapeCollection
{
}
public interface IGroupable
{
}

do name source files according to their main classes. Exception: file names with partial classes          reflect their source or purpose, e.g. designer, generated, etc.

// Located in Task.cs
public partial class Task{
    //...
}
// Located in Task.generated.cs
public partial class Task
{
    //...
}

do organize namespaces with a clearly defined structure

// Examples
namespace Company.Product.Module.SubModule
namespace Product.Module.Component
namespace Product.Layer.Module.Group

do vertically align curly brackets.

// Correct
class Program
{
    static void Main(string[] args)
    {
    }
}

do declare all member variables at the top of a class, with static variables at the very top.

// Correct
public class Account{
    public static string BankName;
    public static decimal Reserves;
    
    public string Number {get; set;}
    public DateTime DateOpened {get; set;}
    public DateTime DateClosed {get; set;}
    public decimal Balance {get; set;}
    
    // Constructor
    public Account()
    {
        // ...
    }
}

do use singular names for enums. Exception: bit field enums.

// Correct
public enum Color{
    Red,
    Green,
    Blue,
    Yellow,
    Magenta,
    Cyan
}
// Exception
[Flags]
public enum Dockings{
    None = 0,
    Top = 1,
    Right = 2,
    Bottom = 4,
    Left = 8
}

do not explicitly specify a type of an enum or values of enums (except bit fields)

// Don't
public enum Direction : long
{
    North = 1,
    East = 2,
    South = 3,
    West = 4
}
// Correct
public enum Direction
{
    North,
    East,
    South,
    West
}

do not suffix enum names with Enum

// Don't
public enum CoinEnum
{
    Penny,
    Nickel,
    Dime,
    Quarter,
    Dollar
}
// Correct
public enum Coin
{
    Penny,
    Nickel
    ,Dime,
    Quarter,
    Dollar
}

Submain Coding Standards

1. Casing

1. **PascalCase**: This is for class names, file names, namespace names, ALL method names, and public member names.
2. **camelCase**: This is used for member names that are not publicly accessible.
3. **UPPER_CASE**: You might also think of this as upper snake case. This is only used for constants.
4. **Pascal_snake_case**: This is used in unit testing, a descriptive method name such as Should_return_2_when_adding_1_and_1 is much easier to read.
Cardinal Casing Sins of C#
  • Hungarian Notation
  • Mixed Conventions

2. Formating

  • Indentation
  • Brackets
  • Terminator
  • Extra Lines

3. “var” Is Your Friend

  • Using var is actually a good idea for all those short-lived variables.

4. Always Use Access Modifiers

  • use the lowest necessary modifier. This is equivalent to the principle of least privilege.

5. Use Auto Properties

public string Name {get; set;}

6. Where to Declare Variables

  • Declare static, const variables at the top of class block
  • Declare temporary variables at the top of method block or where to use it.

7. Code File Organization

8. Use the Aliases, Jack!

9. “using” Is Your Friend!


C# Coding Standard

Naming conventions table

Name Case
Variables camelCase
Class PascalCase
Constructor PascalCase
Properties PascalCase
Delegate PascalCase
Enum PascalCase
Arguments in methods camelCase
Method PascalCase
Constants PascalCase
Field camelCase

Optimize syntax

To declare an empty method which only returns a view in MVC, we should use the expression body.

//Avoid  
public ActionResult Dashboard()
{
    return View();  
}    
//Do  
public ActionResult Dashboard() => View();

To check null or empty condition.

//Avoid  
var varName = "faisal"; 
if (varName != null && varName != "")  
{
    //code  
}    
//Do  
var varName = "faisal";  
if (!string.IsNullOrEmpty(varName))  
{
    //code  
}  

Use null coalescing expression,

Test test = new Test();    
//Avoid  
var varName = test.Name != null ? test.Name : "";    
//Do  
var varName = test.Name ?? "";

Use object initializer,

//Avoid  
Test test = new Test();  
test.Id = 1;  
test.Name = "faisal";    
//Do  
var test = new Test  
{     
    Id = 1,     
    Name = "faisal"  
};  

Use ?. operator,

//Avoid  
var empName = "";  
Session["Name"] = "Faisal Pathan";  
if (Session["Name"] != null)  
{     
    empName = Session["Name"].ToString();    
}
else  
{       
    empName = "";  
}    
//Do  
var empName = "";  
Session["Name"] = "Faisal Pathan";  
empName = Session["Name"]?.ToString() ?? "";  

Avoid extra braces,
Note - only work with single line statements.

var count = 10;    
//Avoid   
if (count > 0)  
{     
    //code     
    count++;  
}      
//Do   
if (count > 0) count++; 
//code      

//Avoid  
for (int i = 0; i < count; i++)  
{     
    //code     
    count += 10;  
}    
//Do  
for (int i = 0; i < count; i++) count += 10;      

var testList = new List<Test>();  
var names = new ArrayList();   
//Avoid  
foreach (var item in testList)  
{     
    names.Add(item.Name);  
}    
//Do  
foreach (var item in testList) names.Add(item.Name); 

Use string interpolation.

 Test test = new Test();    
 //Avoid   
 var details = string.Format("{0}, you are welcome, Your Id is {1}", test.Name , test.Id + "_emp");    
 //Do  
 var details = $"{test.Name}, you are welcome, Your Id is {test.Id}_emp";  

New lightweight switchcase with c# 8,

int itemSwitch = 1;    
//Good  
switch (itemSwitch)  {   
    case 1:   
        Console.WriteLine("Item 1");   
        break;   
    case 2:   
        Console.WriteLine("Item 2");   
        break; 
    case 3:  
        Console.WriteLine("Item 3"); 
        break; 
    default:   
        Console.WriteLine("Default item case");   
        break;  
}    
//better   
var message = itemSwitch switch               
{
    1 =>  Console.WriteLine("Item 1"),
    2 =>  Console.WriteLine("Item 2"),
    3 =>  Console.WriteLine("Item 3") ,
    _ => "Default item case"            
};  
Console.WriteLine(message);

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). For more information, see Options, Text Editor, C#, Formatting.
  • 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.
if ((val1 > val2) && (val1 > val3))
{ 
    // Take appropriate action. 
}

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.
// The following declaration creates a query. It does not run 
// the query.
  • Do not create formatted blocks of asterisks around comments.

Language Guidelines

The following sections describe practices that the C# team follows to prepare code examples and samples.

String Data Type

  • Use string interpolation to concatenate short strings, as shown in the following code.
string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
  • To append strings in loops, especially when you are working with large amounts of text, use a StringBuilder object.
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; 
var manyPhrases = new StringBuilder(); 
for (var i = 0; i < 10000; i++) 
{ 
    manyPhrases.Append(phrase); 
} 
//Console.WriteLine("tra" + manyPhrases);

Implicitly Typed Local Variables

  • Use implicit typing for local variables when the type of the variable is obvious from the right side of the assignment, or when the precise type is not important.
// When the type of a variable is clear from the context, use var 
// in the declaration. 
var var1 = "This is clearly a string."; 
var var2 = 27; 
var var3 = Convert.ToInt32(Console.ReadLine());
  • Do not use var when the type is not apparent from the right side of the assignment.
// When the type of a variable is not clear from the context, use an 
// explicit type. 
int var4 = ExampleClass.ResultSoFar();
  • Do not rely on the variable name to specify the type of the variable. It might not be correct.
// Naming the following variable inputInt is misleading. 
// It is a string. 
var inputInt = Console.ReadLine(); 
Console.WriteLine(inputInt);
  • Avoid the use of var in place of dynamic.
  • Use implicit typing to determine the type of the loop variable in for loops.

The following example uses implicit typing in a for statement.

var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; 
var manyPhrases = new StringBuilder(); 
for (var i = 0; i < 10000; i++) 
{ 
    manyPhrases.Append(phrase); 
} 
//Console.WriteLine("tra" + manyPhrases);
  • Do not use implicit typing to determine the type of the loop variable in foreach loops.The following example uses explicit typing in a foreach statement.
foreach (var ch in laugh) 
{ 
    if (ch == 'h') 
        Console.Write("H"); 
    else 
        Console.Write(ch); 
} 
Console.WriteLine();

Note
Be careful not to accidentally change a type of an element of the iterable collection. For example, it is easy to switch from System.Linq.IQueryable to System.Collections.IEnumerable in a foreach statement, which changes the execution of a query.

Unsigned Data Type

In general, use int rather than unsigned types. The use of int is common throughout C#, and it is easier to interact with other libraries when you use int.

Arrays

Use the concise syntax when you initialize arrays on the declaration line.

// Preferred syntax. Note that you cannot use var here instead of string[].
string[] vowels1 = { "a", "e", "i", "o", "u" };

// If you use explicit instantiation, you can use var.
var vowels2 = new string[] { "a", "e", "i", "o", "u" };

// If you specify an array size, you must initialize the elements one at a time.
var vowels3 = new string[5];
vowels3[0] = "a";
vowels3[1] = "e";
// And so on.

Delegates

Use the concise syntax to create instances of a delegate type.

// First, in class Program, define the delegate type and a method that
// has a matching signature.

// Define the type.
public delegate void Del(string message);

// Define a method that has a matching signature.
public static void DelMethod(string str)
{
    Console.WriteLine("DelMethod argument: {0}", str);
}
// In the Main method, create an instance of Del.

// Preferred: Create an instance of Del by using condensed syntax.
Del exampleDel2 = DelMethod;

// The following declaration uses the full syntax.
Del exampleDel1 = new Del(DelMethod);

try-catch and using Statements in Exception Handling

  • Use a try-catch statement for most exception handling.
static string GetValueFromArray(string[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (System.IndexOutOfRangeException ex)
    {
        Console.WriteLine("Index is out of range: {0}", index);
        throw;
    }
}
  • 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.
// This try-finally statement only calls Dispose in the finally block.
Font font1 = new Font("Arial", 10.0f);
try
{
    byte charset = font1.GdiCharSet;
}
finally
{
    if (font1 != null)
    {
        ((IDisposable)font1).Dispose();
    }
}

// You can do the same thing with a using statement.
using (Font font2 = new Font("Arial", 10.0f))
{
    byte charset = font2.GdiCharSet;
}

&& and || Operators

To avoid exceptions and increase performance by skipping unnecessary comparisons, use && instead of & and || instead of | when you perform comparisons, as shown in the following example.

Console.Write("Enter a dividend: ");
var dividend = Convert.ToInt32(Console.ReadLine());

Console.Write("Enter a divisor: ");
var divisor = Convert.ToInt32(Console.ReadLine());

// If the divisor is 0, the second clause in the following condition
// causes a run-time error. The && operator short circuits when the
// first expression is false. That is, it does not evaluate the
// second expression. The & operator evaluates both, and causes
// a run-time error when divisor is 0.
if ((divisor != 0) && (dividend / divisor > 0))
{
    Console.WriteLine("Quotient: {0}", dividend / divisor);
}
else
{
    Console.WriteLine("Attempted division by 0 ends up here.");
}

New Operator

  • Use the concise form of object instantiation, with implicit typing, as shown in the following declaration.
var instance1 = new ExampleClass();

The previous line is equivalent to the following declaration.

ExampleClass instance2 = new ExampleClass();
  • Use object initializers to simplify object creation.
// Object initializer.
var instance3 = new ExampleClass { Name = "Desktop", ID = 37414,
    Location = "Redmond", Age = 2.3 };

// Default constructor and assignment statements.
var instance4 = new ExampleClass();
instance4.Name = "Desktop";
instance4.ID = 37414;
instance4.Location = "Redmond";
instance4.Age = 2.3;

Event Handling

If you are defining an event handler that you do not need to remove later, use a lambda expression.

public Form2()
{
    // You can use a lambda expression to define an event handler.
    this.Click += (s, e) =>
        {
            MessageBox.Show(
                ((MouseEventArgs)e).Location.ToString());
        };
}
// Using a lambda expression shortens the following traditional definition.
public Form1()
{
    this.Click += new EventHandler(Form1_Click);
}

void Form1_Click(object sender, EventArgs e)
{
    MessageBox.Show(((MouseEventArgs)e).Location.ToString());
}

Static Members

Call static members by using the class name: ClassName.StaticMember. This practice makes code more readable by making static access clear. Do not qualify a static member defined in a base class with the name of a derived class. While that code compiles, the code readability is misleading, and the code may break in the future if you add a static member with the same name to the derived class.

LINQ Queries

  • Use meaningful names for query variables. The following example uses seattleCustomers for customers who are located in Seattle.
var seattleCustomers = from customer in customers
                       where customer.City == "Seattle"
                       select customer.Name;
  • Use aliases to make sure that property names of anonymous types are correctly capitalized, using Pascal casing.
var localDistributors =
    from customer in customers
    join distributor in distributors on customer.City equals distributor.City
    select new { Customer = customer, Distributor = distributor };
  • Rename properties when the property names in the result would be ambiguous. For example, if your query returns a customer name and a distributor ID, instead of leaving them as Name and ID in the result, rename them to clarify that Name is the name of a customer, and ID is the ID of a distributor.
var localDistributors2 =
    from customer in customers
    join distributor in distributors on customer.City equals distributor.City
    select new { CustomerName = customer.Name, DistributorID = distributor.ID };
  • Use implicit typing in the declaration of query variables and range variables.
var seattleCustomers = from customer in customers
                       where customer.City == "Seattle"
                       select customer.Name;
  • Align query clauses under the from clause, as shown in the previous examples.
  • Use where clauses before other query clauses to ensure that later query clauses operate on the reduced, filtered set of data.
var seattleCustomers2 = from customer in customers
                        where customer.City == "Seattle"
                        orderby customer.Name
                        select customer;
  • Use multiple from clauses instead of a join clause to access inner collections. For example, a collection of Student objects might each contain a collection of test scores. When the following query is executed, it returns each score that is over 90, along with the last name of the student who received the score.
// Use a compound from to access the inner sequence within each element.
var scoreQuery = from student in students
                 from score in student.Scores
                 where score > 90
                 select new { Last = student.LastName, score };
posted @ 2022-03-31 09:40  桁椽  阅读(212)  评论(0编辑  收藏  举报