This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can trysigning in orchanging directories.
Access to this page requires authorization. You can trychanging directories.
Coding conventions are essential for maintaining code readability, consistency, and collaboration within a development team. Code that follows industry practices and established guidelines is easier to understand, maintain, and extend. Most projects enforce a consistent style through code conventions. Thedotnet/docs
anddotnet/samples
projects are no exception. In this series of articles, you learn our coding conventions and the tools we use to enforce them. You can take our conventions as-is, or modify them to suit your team's needs.
We chose our conventions based on the following goals:
Important
These guidelines are used by Microsoft to develop samples and documentation. They were adopted from the.NET Runtime, C# Coding Style andC# compiler (roslyn) guidelines. We chose those guidelines because of their adoption over several years of Open Source development. These guidelines help community members participate in the runtime and compiler projects. They're meant to be an example of common C# conventions, and not an authoritative list (seeFramework Design Guidelines for detailed guidelines).
Theteaching andadoption goals are why the docs coding convention differs from the runtime and compiler conventions. Both the runtime and compiler have strict performance metrics for hot paths. Many other applications don't. Ourteaching goal mandates that we don't prohibit any construct. Instead, samples show when constructs should be used. We update samples more aggressively than most production applications do. Ouradoption goal mandates that we show code you should write today, even when code written last year doesn't need changes.
This article explains our guidelines. The guidelines evolve over time, and you'll find samples that don't follow our guidelines. We welcome PRs that bring those samples into compliance, or issues that draw our attention to samples we should update. Our guidelines are Open Source and we welcome PRs and issues. However, if your submission would change these recommendations, open an issue for discussion first. You're welcome to use our guidelines, or adapt them to your needs.
Tools can help your team enforce your conventions. You can enablecode analysis to enforce the rules you prefer. You can also create aneditorconfig so that Visual Studio automatically enforces your style guidelines. As a starting point, you can copy thedotnet/docs
.editorconfig to use our style.
These tools make it easier for your team to adopt your preferred guidelines. Visual Studio applies the rules in all.editorconfig files in scope to format your code. You can use multiple configurations to enforce corporate-wide conventions, team conventions, and even granular project conventions.
Code analysis produces warnings and diagnostics when it detects rule violations. You configure the rules you want applied to your project. Then, each CI build notifies developers when they violate any of the rules.
The following sections describe practices that the .NET docs team follows to prepare code examples and samples. In general, follow these practices:
string
instead ofSystem.String, orint
instead ofSystem.Int32. This recommendation includes using the typesnint
andnuint
.int
rather than unsigned types. The use ofint
is common throughout C#, and it's easier to interact with other libraries when you useint
. Exceptions are for documentation specific to unsigned data types.var
only when a reader can infer the type from the expression. Readers view our samples on the docs platform. They don't have hover or tool tips that display the type of variables.More specific guidelines follow.
Usestring 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're working with large amounts of text, use aSystem.Text.StringBuilder object.
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";var manyPhrases = new StringBuilder();for (var i = 0; i < 10000; i++){ manyPhrases.Append(phrase);}//Console.WriteLine("tra" + manyPhrases);
Prefer raw string literals to escape sequences or verbatim strings.
var message = """ This is a long message that spans across multiple lines. It uses raw string literals. This means we can also include characters like \n and \t without escaping them. """;
Use the expression-based string interpolation rather than positional string interpolation.
// Execute the queries.Console.WriteLine("scoreQuery:");foreach (var student in scoreQuery){ Console.WriteLine($"{student.Last} Score: {student.score}");}
Use Pascal case for primary constructor parameters on record types:
public record Person(string FirstName, string LastName);
Use camel case for primary constructor parameters on class and struct types.
Userequired
properties instead of constructors to force initialization of property values:
public class LabelledContainer<T>(string label){ public string Label { get; } = label; public required T Contents { get; init; }}
string[] vowels = [ "a", "e", "i", "o", "u" ];
Func<>
andAction<>
instead of defining delegate types. In a class, define the delegate method.Action<string> actionExample1 = x => Console.WriteLine($"x is: {x}");Action<string, string> actionExample2 = (x, y) => Console.WriteLine($"x is: {x}, y is {y}");Func<string, int> funcExample1 = x => Convert.ToInt32(x);Func<int, int, int> funcExample2 = (x, y) => x + y;
Func<>
orAction<>
delegate.actionExample1("string for x");actionExample2("string for x", "string for y");Console.WriteLine($"The value is {funcExample1("1")}");Console.WriteLine($"The sum is {funcExample2(1, 2)}");
If you create instances of a delegate type, use the concise syntax. In a class, define the delegate type and a method that has a matching signature.
public delegate void Del(string message);public static void DelMethod(string str){ Console.WriteLine($"DelMethod argument: {str}");}
Create an instance of the delegate type and call it. The following declaration shows the condensed syntax.
Del exampleDel2 = DelMethod;exampleDel2("Hey");
The following declaration uses the full syntax.
Del exampleDel1 = new Del(DelMethod);exampleDel1("Hey");
try-catch
andusing
statements in exception handlingUse atry-catch statement for most exception handling.
static double ComputeDistance(double x1, double y1, double x2, double y2){ try { return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } catch (System.ArithmeticException ex) { Console.WriteLine($"Arithmetic overflow or underflow: {ex}"); throw; }}
Simplify your code by using the C#using statement. If you have atry-finally statement in which the only code in thefinally
block is a call to theDispose method, use ausing
statement instead.
In the following example, thetry-finally
statement only callsDispose
in thefinally
block.
Font bodyStyle = new Font("Arial", 10.0f);try{ byte charset = bodyStyle.GdiCharSet;}finally{ bodyStyle?.Dispose();}
You can do the same thing with ausing
statement.
using (Font arial = new Font("Arial", 10.0f)){ byte charset2 = arial.GdiCharSet;}
Use the newusing
syntax that doesn't require braces:
using Font normalStyle = new Font("Arial", 10.0f);byte charset3 = normalStyle.GdiCharSet;
&&
and||
operatorsUse&&
instead of&
and||
instead of|
when you perform comparisons, as shown in the following example.
Console.Write("Enter a dividend: ");int dividend = Convert.ToInt32(Console.ReadLine());Console.Write("Enter a divisor: ");int divisor = Convert.ToInt32(Console.ReadLine());if ((divisor != 0) && (dividend / divisor) is var result){ Console.WriteLine($"Quotient: {result}");}else{ Console.WriteLine("Attempted division by 0 ends up here.");}
If the divisor is 0, the second clause in theif
statement would cause a run-time error. But the && operator short-circuits when the first expression is false. That is, it doesn't evaluate the second expression. The & operator would evaluate both, resulting in a run-time error whendivisor
is 0.
new
operatorUse one of the concise forms of object instantiation when the variable type matches the object type, as shown in the following declarations. This form isn't valid when the variable is an interface type, or a base class of the runtime type.
var firstExample = new ExampleClass();
ExampleClass instance2 = new();
The preceding declarations are equivalent to the following declaration.
ExampleClass secondExample = new ExampleClass();
Use object initializers to simplify object creation, as shown in the following example.
var thirdExample = new ExampleClass { Name = "Desktop", ID = 37414, Location = "Redmond", Age = 2.3 };
The following example sets the same properties as the preceding example but doesn't use initializers.
var fourthExample = new ExampleClass();fourthExample.Name = "Desktop";fourthExample.ID = 37414;fourthExample.Location = "Redmond";fourthExample.Age = 2.3;
public Form2(){ this.Click += (s, e) => { MessageBox.Show( ((MouseEventArgs)e).Location.ToString()); };}
The 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());}
Callstatic members by using the class name:ClassName.StaticMember. This practice makes code more readable by making static access clear. Don't 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 might break in the future if you add a static member with the same name to the derived class.
Use meaningful names for query variables. The following example usesseattleCustomers
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 name, instead of leaving them as a form ofName
in the result, rename them to clarifyCustomerName
is the name of a customer, andDistributorName
is the name of a distributor.
var localDistributors2 = from customer in Customers join distributor in Distributors on customer.City equals distributor.City select new { CustomerName = customer.Name, DistributorName = distributor.Name };
Use implicit typing in the declaration of query variables and range variables. This guidance on implicit typing in LINQ queries overrides the general rules forimplicitly typed local variables. LINQ queries often use projections that create anonymous types. Other query expressions create results with nested generic types. Implicit typed variables are often more readable.
var seattleCustomers = from customer in Customers where customer.City == "Seattle" select customer.Name;
Align query clauses under thefrom
clause, as shown in the previous examples.
Usewhere
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;
Access inner collections with multiplefrom
clauses instead of ajoin
clause. For example, a collection ofStudent
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 family name of the student who received the score.
var scoreQuery = from student in students from score in student.Scores where score > 90 select new { Last = student.LastName, score };
Useimplicit typing for local variables when the type of the variable is obvious from the right side of the assignment.
var message = "This is clearly a string.";var currentTemperature = 27;
Don't usevar when the type isn't apparent from the right side of the assignment. Don't assume the type is clear from a method name. A variable type is considered clear if it's anew
operator, an explicit cast, or assignment to a literal value.
int numberOfIterations = Convert.ToInt32(Console.ReadLine());int currentMaximum = ExampleClass.ResultSoFar();
Don't use variable names to specify the type of the variable. It might not be correct. Instead, use the type to specify the type, and use the variable name to indicate the semantic information of the variable. The following example should usestring
for the type and something likeiterations
to indicate the meaning of the information read from the console.
var inputInt = Console.ReadLine();Console.WriteLine(inputInt);
Avoid the use ofvar
in place ofdynamic. Usedynamic
when you want run-time type inference. For more information, seeUsing type dynamic (C# Programming Guide).
Use implicit typing for the loop variable infor
loops.
The following example uses implicit typing in afor
statement.
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";var manyPhrases = new StringBuilder();for (var i = 0; i < 10000; i++){ manyPhrases.Append(phrase);}//Console.WriteLine("tra" + manyPhrases);
Don't use implicit typing to determine the type of the loop variable inforeach
loops. In most cases, the type of elements in the collection isn't immediately obvious. The collection's name shouldn't be solely relied upon for inferring the type of its elements.
The following example uses explicit typing in aforeach
statement.
foreach (char ch in laugh){ if (ch == 'h') { Console.Write("H"); } else { Console.Write(ch); }}Console.WriteLine();
use implicit type for the result sequences in LINQ queries. The section onLINQ explains that many LINQ queries result in anonymous types where implicit types must be used. Other queries result in nested generic types wherevar
is more readable.
Note
Be careful not to accidentally change a type of an element of the iterable collection. For example, it's easy to switch fromSystem.Linq.IQueryable toSystem.Collections.IEnumerable in aforeach
statement, which changes the execution of a query.
Some of our samples explain thenatural type of an expression. Those samples must usevar
so that the compiler picks the natural type. Even though those examples are less obvious, the use ofvar
is required for the sample. The text should explain the behavior.
Most code files declare a single namespace. Therefore, our examples should use the file scoped namespace declarations:
namespace MySampleCode;
When ausing
directive is outside a namespace declaration, that imported namespace is its fully qualified name. The fully qualified name is clearer. When theusing
directive is inside the namespace, it could be either relative to that namespace, or its fully qualified name.
using Azure;namespace CoolStuff.AwesomeFeature{ public class Awesome { public void Stuff() { WaitUntil wait = WaitUntil.Completed; // ... } }}
Assuming there's a reference (direct, or indirect) to theWaitUntil class.
Now, let's change it slightly:
namespace CoolStuff.AwesomeFeature{ using Azure; public class Awesome { public void Stuff() { WaitUntil wait = WaitUntil.Completed; // ... } }}
And it compiles today. And tomorrow. But then sometime next week the preceding (untouched) code fails with two errors:
- error CS0246: The type or namespace name 'WaitUntil' could not be found (are you missing a using directive or an assembly reference?)- error CS0103: The name 'WaitUntil' does not exist in the current context
One of the dependencies introduced this class in a namespace then ends with.Azure
:
namespace CoolStuff.Azure{ public class SecretsManagement { public string FetchFromKeyVault(string vaultId, string secretId) { return null; } }}
Ausing
directive placed inside a namespace is context-sensitive and complicates name resolution. In this example, it's the first namespace that it finds.
CoolStuff.AwesomeFeature.Azure
CoolStuff.Azure
Azure
Adding a new namespace that matches eitherCoolStuff.Azure
orCoolStuff.AwesomeFeature.Azure
would match before the globalAzure
namespace. You could resolve it by adding theglobal::
modifier to theusing
declaration. However, it's easier to placeusing
declarations outside the namespace instead.
namespace CoolStuff.AwesomeFeature{ using global::Azure; public class Awesome { public void Stuff() { WaitUntil wait = WaitUntil.Completed; // ... } }}
In general, use the following format for code samples:
Use single-line comments (//
) for brief explanations.
Avoid multi-line comments (/* */
) for longer explanations.
Comments in the code samples aren't localized. That means explanations embedded in the code aren't translated. Longer, explanatory text should be placed in the companion article, so that it can be localized.
For describing methods, classes, fields, and all public members useXML comments.
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.
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, seeOptions, Text Editor, C#, Formatting.
Write only one statement per line.
Write only one declaration per line.
If continuation lines aren't 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 ((startX > endX) && (startX > previousX)){ // Take appropriate action.}
Exceptions are when the sample explains operator or expression precedence.
Follow the guidelines inSecure Coding Guidelines.
Was this page helpful?
Was this page helpful?