Unlike the Framework design guidelines, these coding style conventions are not required and should be treated as a set of suggestions. The reason we don’t insist on following these coding conventions is that they have no direct effect on most users of a framework.
There are many coding style conventions, each with its own history and philosophy. The conventions described here are, more or less, the conventions used by the .NET BCL team since moving .NET to open source. Since many other projects have set their coding style based on the .NET BCL team coding style guidelines, the third edition of this book replaced the previous contents of this appendix with this new de facto standard.
Since most developers have an easier time understanding, and contributing to, codebases that use a style with which they are already familiar, we encourage you to use this appendix as a starting point in making coding style conventions for any new open-source C# project that you create.
The style conventions here start from a few principles:
If there’s a choice between improving the clarity for the reader and the brevity of the writer, clarity wins (for example, var
is only used in limited situations). This recognizes that code is read more than it is written and that a code reviewer typically has less context than a code author.
If there are two ways to express something, the one that reduces noise in a future change is preferred (for example, add the trailing comma after the last declared value in an enum).
The conventions are supposed to be simple to follow. This means both that there are few of them, and that style rules that need fewer exceptions are preferred. For example, Allman-style bracing rules have fewer exceptions than K&R-style bracing rules.
Please note that the numbered chapters in this book do not follow these guidelines. Instead, they use a more compact, “paper-friendly” style.
The .NET BCL team uses Allman-style bracing. Braces are generally required, even when the language considers them optional, with a few exceptions described in the guidance.
DO place the opening brace on the next line, indented to the same level as the block statement.
if (someExpression) { DoSomething(); }
DO align the closing brace with the opening brace.
if (someExpression) { DoSomething(); }
DO place the closing brace on its own line, except the end of a do
..while
statement.
if (someExpression) { do { DoSomething(); } while(someOtherCondition); } else if (someOtherExpression) { ... }
AVOID omitting braces, even if the language allows it.
Braces should not be considered optional. Even for single-statement blocks, you should use braces. This increases code readability and maintainability.
for (int i = ʘ; i < 1ʘʘ; i++) { DoSomething(i); }
One exception to the rule is braces in case
statements. These braces can be omitted, because the case
and break
statements indicate the beginning and the end of the block.
case ʘ: DoSomething(); break;
CONSIDER omitting braces in an argument validation preamble.
For all parts of the method preamble that are of the form if (single-LineExpression) { throw … }
, the braces and vertical whitespace can be eliminated. Once you add a blank line, or have code other than if
…throw
, you’ve left the preamble and braces become required again. Multi-line conditions should ideally be factored out into methods to maintain the visual flow of the preamble.
public void DoSomething(SomeCollection coll, int start, int count) { if (coll is null) throw new ArgumentNullException(nameof(coll)); if ((uint)start < (uint)coll.Count) throw new ArgumentOutOfRangeException(nameof(start)); if (coll.Count – count > start) throw new ArgumentOutOfRangeException(nameof(count)); if (count == ʘ) { return; } ... }
DO NOT use the braceless variant of the using
(dispose) statement.
The braced using
statement provides a strong visual indicator of when the value is being released. Just as with locking, a disposable value should be disposed as soon as it is practical to do so. The braceless variant makes it too easy to add code that unnecessarily extends the scope of the disposable value, and changing the braceless version to the braced version adds unnecessary noise to a diff
representation of a change.
Right: using (Element element = GetElement()) { ... } Wrong: using Element element = GetElement(); ...
AVOID using the braceless variant of the await using
statement, except to simulate stacked await using
statements with ConfigureAwait
.
Until an addition to the C# language allows for simple stacked await using
statements while also appropriately using ConfigureAwait
(discussed in Chapter 9, sections 9.2.6.2 and 9.4.4.1), the braceless await using
can be used to achieve the same level of conciseness, provided it is used only in a series of variable declarations and await using
statements in a fresh scope.
The braceless await using statement, as of C# 8.0, requires a variable declaration, but since the result of ConfigureAwait
—a Configured-AsyncDisposable
value—is never going to be directly used in your method, you can use “var ignored” as an exception to the normal restrictions on the use of the var
keyword.
Acceptable: { Element element1 = GetElement(1); await using var ignored1 = element1.ConfigureAwait(false); Element element2 = GetElement(2); await using var ignored 2 = element2.ConfigureAwait(false); ... } Wrong: await using Element element1 = GetElement(1); await using Element element2 = GetElement(2); ...
DO use one space before and after the opening and closing braces when they share a line with other code. Do not add a trailing space before a line break.
public int Foo { get { return foo; } }
DO use a single space after a comma between parameters.
Right: public void Foo(char bar, int x, int y) Wrong: public void Foo(char bar,int x,int y)
DO use a single space between arguments.
Right: Foo(myChar, ʘ, 1) Wrong: Foo(myChar,ʘ,1)
DO NOT use spaces after the opening parenthesis or before the closing parenthesis.
Right: Foo(myChar, ʘ, 1) Wrong: Foo( myChar, ʘ, 1 )
DO NOT use spaces between a member name and an opening parenthesis.
Right: Foo() Wrong: Foo ()
DO NOT use spaces after or before square braces.
Right: x = dataArray[index]; Wrong: x = dataArray[ index ];
DO use spaces between flow control keywords and the open parenthesis.
Right: while (x == y) Wrong: while(x == y)
DO use spaces before and after binary operators.
Right: if (x == y) { ... } Wrong: if (x==y) { ... }
DO NOT use spaces before or after unary operators.
Right: if (!y){ ... } Wrong: if (! y){ ... }
DO use four consecutive space characters for indents.
DO NOT use the tab character for indents.
DO indent contents of code blocks.
if (someExpression) { DoSomething(); }
DO indent case
blocks even if not using braces.
switch (someExpression) { case ʘ: DoSomething(); break; ... }
DO “outdent” goto
labels one indentation level.
private void SomeMethod() { int iteration = ʘ; start: ... if (...) { nested_label: iteration++; goto start; } ... }
DO indent all continued lines of a single statement one indentation level.
bool firstResult = list.Count > ʘ ? list[list.Length – 1} > list[ʘ] : list.Capacity > 1ʘʘʘ; string complex = _table[index].Property.Method(methodParameter). Method2(method2Parameter, method2Parameter2). Property2;
DO chop before the first argument or parameter, indent one indentation level, and include one argument or parameter per line when a method declaration or method invocation is exceedingly long.
private void SomeMethod( int firstParameter, string secondParameter, bool thirdParameter, string fourthParameter) { ... SomeOtherMethod( firstParameter * 1ʘʘ, fourthParameter, thirdParameter && secondParameter.Length > fourthParameter.Length, 22); ... }
DO add a blank line before control flow statements.
DO add a blank line after a closing brace, unless the next line is also a closing brace.
while (!queue.IsEmpty) { Element toProcess = queue.Pop(); if (toProcess == null) { continue; } if (!toProcess.IsDone) { ... } }
DO add a blank line after “paragraphs” of code where it enhances readability.
switch (someExpression) { case ʘxs: DoSomething(); break; ... }
The .NET BCL team considers this the canonical order of modifiers: public
, private
, protected
, internal
, static
, extern
, new
, virtual
, abstract
, sealed
, override
, readonly
, unsafe
, volatile
, async
. The guidelines and examples are mostly just expressing this in more detail.
DO always explicitly specify visibility modifiers.
Right: internal class SomeClass { private object _lockObject; ... } Wrong: class SomeClass { object _lockObject; ... }
DO specify the visibility modifier as the first modifier.
Right: protected abstract void Reset(); Wrong: abstract protected void Reset();
DO specify the static
modifier immediately after visibility, for static members or static classes.
Right: private static readonly ConcurrentQueue<T> ... Wrong: private readonly static ConcurrentQueue<T> ...
DO specify the extern
method modifier immediately after the static
modifier for an extern
method.
Right: private static extern IntPtr CreateFile(...); Wrong: private extern static IntPtr CreateFile(...);
DO specify the member slot modifier (new
, virtual
, abstract
, sealed
, or override
) immediately after the static
modifier (if specified).
Right: public static new SomeType Create(...) Wrong: public new static SomeType Create(...)
DO specify the readonly
modifier for fields or methods immediately after the member slot modifier (if specified).
Right: private new readonly object _lockObject = new object(); Wrong: private readonly new object _lockObject = new object();
DO specify the unsafe
modifier for methods immediately after the readonly
modifier (if specified).
Right: public readonly unsafe Matrix Multiply(...) Wrong: public unsafe readonly Matrix Multiply(...)
DO specify the volatile
modifier for fields immediately after the static
modifier (if specified).
This guideline skips the readonly
modifier for fields since volatile
and readonly
are mutually exclusive modifiers.
Right: private static volatile int s_instanceCount; Wrong: private volatile static int s_instanceCount;
DO specify the async
modifier as the last method modifier, when used.
Right: public readonly async Task CloseAsync(...) Wrong: public async readonly Task CloseAsync(...)
DO use “protected internal
” instead of “internal protected
” for a member that can be called by derived types or types in the same assembly.
Right: protected internal void Reset() Wrong: internal protected void Reset()
DO use “private protected
” instead of “protected private
” for a member that can be called by derived types within the same assembly.
Right: private protected void Reset() Wrong: protected private void Reset()
DO add the optional trailing comma after the last enum member.
DO add the optional trailing comma after a property assignment from an object initializer, or addition via a collection initializer, when the initializer spans multiple lines.
Providing the trailing comma avoids unnecessary lines in a diff
view of a change, where the formerly last line gains a comma.
Right: public enum SomeEnum { First, Second, Third, } Wrong: public enum SomeEnum { First, Second, Third }
DO NOT use this.
unless absolutely necessary.
If the private members are named correctly, a “this.
” should not be required except to invoke an extension method. The use of the underscore prefix on fields already provides a distinction between field access and variable access.
DO use language keywords (string
, int
, double
, …) instead of BCL type names (String
, Int32
, Double
, …), for both variable declarations and method invocation.
Right: string[] split = string.Split(message, Delimiters); int count = split.Length; Wrong: String split = String.Split(message, Delimiters); Int32 count = split.Length;
DO NOT use the var
keyword except to save the result of a new
, as
-cast or “hard” cast.
In the core .NET libraries, we only use var
when the type name is already specified. We don’t use it when calling factory methods, calling methods for which “everyone knows” the return type (e.g., String.IndexOf
), or receiving the result of a TryParse
method. We also do not compel the use of var
when using a new
, as
-cast, or “hard” cast; instead, we leave it as a judgment call on the part of the code author.
Acceptable (no var): StringBuilder builder = new StringBuilder(); List<int> list = collection as List<int>; string built = ProcessList(list, builder); SomeType fromCache = cache.Get<SomeType>(); if (int.TryParse(input, out int parsedValue) { ... } Acceptable (maximal use of var): var builder = new StringBuilder(); var list = collection as List<int>; string built = ProcessList(list, builder); SomeType fromCache = cache.Get<SomeType>(); if (int.TryParse(input, out int parsedValue) { ... } Wrong (uses var in places that are not permitted): var builder = new StringBuilder(); var list = collection as List<int>; var built = ProcessList(list, builder); var fromCache = cache.Get<SomeType>(); if (int.TryParse(input, out var parsedValue) { ... }
DO use object initializers where possible.
This rule can be ignored when working with a type that has a required order of property assignment. As with most exceptions to a rule, however, a comment should explain why the code should not be changed to use object initializers. The object initializer does respect the order the properties are assigned, but it doesn’t “feel” as ordered as multiple assignment statements.
Right: SomeType someType = new SomeType { PropA = ..., PropB = ..., PropC = ..., } Acceptable: // PropB has to be set first SomeType someType = new SomeType { PropB = ..., }; someType.PropA = ...; someType.PropC = ...; Not preferred: SomeType someType = new SomeType(); someType.PropB = ...; someType.PropA = ...; someType.PropC = ...;
DO use collection initializers where possible.
Right: SomeCollection someColl = new SomeCollection { first, second, third, }; Not preferred: SomeCollection someColl = new SomeCollection(); someColl.Add(first); someColl.Add(second); someColl.Add(third);
CONSIDER using expression-bodied members when the implementation of a property or method is unlikely to change.
public partial class UnseekableStream : Stream { public bool CanSeek => false; public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); }
DO use auto-implemented properties when the implementation is unlikely to change.
internal partial class SomeCache { internal long HitCount { get; private set; } internal long MissCount { get; private set; } }
DO restrict code to ASCII characters, and use Unicode escape sequences (uXXXX
) when needed for non-ASCII values.
Following the naming rules from Chapter 3 means that a class representing a résumé (a one-sheet description of someone’s work and education history) would be named “Resume” (without the diacritical marks). If you still want to name it “Résumé,” the e-acute can be written as “uʘʘE9
”.
public partial class RuʘʘE9sumuʘʘE9 { ... } ... string nihongo = "u65E5u672Cu8A9E";
DO use the nameof(...)
syntax, instead of a literal string, when referring to the name of a type, member, or parameter.
Right: throw new ArgumentNullException(nameof(target)); Wrong: throw new ArgumentNullException("target");
DO apply the readonly
modifier to fields when possible.
Be careful when applying the readonly
modifier to fields where the field type is a struct
. If the struct isn’t declared as readonly
, then property and method invocations might make local copies of the value and create a performance problem.
DO use if
…throw
instead of assignment–expression–throw for argument validation.
Using expression–throw in an assignment statement can lead to subtle bugs in finalizers where some fields are set but others aren’t. While finalizers aren’t common, always using if
…throw
generalizes without exception to types with and without finalizers, as well as to both methods and constructors.
Right: public SomeType(SomeOtherType target, ...) { if (target is null) throw new ArgumentNullException(nameof(target)); ... _target = target; ... } Wrong: public SomeType(SomeOtherType target, ...) { _target = target ?? throw new ArgumentNullException(nameof(target)); ... }
In general, we recommend following the Framework Design Guidelines for naming identifiers. However, some additional conventions and exceptions to using the Framework Design Guidelines arise when naming internal and private identifiers.
DO follow the Framework Design Guidelines for naming identifiers, except for naming private and internal fields.
DO use PascalCasing for namespace, type, and member names, except for internal and private fields.
namespace Your.MultiWord.Namespace { public abstract class SomeClass { internal int SomeProperty { get; private set; } ... } }
DO use PascalCasing for const locals and const fields except for interop code, where it should match the name from the called code exactly.
internal class SomeClass { private const DefaultListSize = 32; private bool Encode() { const int RetryCount = 3; const int ERROR_MORE_DATA = ʘxEA; ... } }
DO use camelCasing for private and internal fields.
DO use a prefix “_” (underscore) for private and internal instance fields, “s_” for private and internal static fields, and “t_” for private and internal thread-static fields.
These prefixes are very valuable to a code reviewer. Reading from, or writing to, a field indicates why a method isn’t declared as static
. Writes to a field indicate a persisted state. Reading from, or writing to, a static field indicates a shared persisted state and a potential thread-safety problem. Thread-static fields require even more attention, because they may lose their value after an await
—or have never been initialized on this thread.
internal partial class SomeClass { [ThreadStatic] private static byte[] t_buffer; private static int s_instanceCount; private int _instanceId; }
DO use camelCasing for local variables.
DO use camelCasing for parameters.
DO NOT use Hungarian notation (i.e., do not encode the type of a variable in its name).
Comments should be used to describe the intent, algorithmic overview, and logical flow. It would be ideal if, from reading the comments alone, someone other than the author could understand the function’s behavior and purpose. Although there are no minimum comment requirements and certainly some very small routines need no commenting at all, it is desirable for most nontrivial routines to have comments reflecting the programmer’s intent and approach.
DO NOT use comments unless they describe something not obvious to someone other than the developer who wrote the code.
AVOID multiline syntax (/* ... */
) for comments. The single-line syntax (// ...
) is preferred even when a comment spans multiple lines.
// Implements a variable-size list that uses an array of objects // to store the elements. A List has a capacity, which is the // allocated length of the internal array. As elements are added // to a List, the capacity of the List is automatically increased // as required by reallocating the internal array. // public class List<T> : IList<T>, IList { ... }
DO NOT place comments at the end of a line unless the comment is very short.
AVOID placing comments at the end of a line even when the comment is very short.
//Avoid public class ArrayList { private int count; // -1 indicates uninitialized array }
AVOID writing “I” in comments.
Code ownership changes over time, particularly in long-lived software projects, which leaves “I” ambiguous when not consulting code history.
Rather than “I found that …,” say something like “Based on the 2019 aggregated TPS report ….”
Use “we” to refer to the team or project, collectively. “We already checked for null, so ….”
Consider personifying the method: “This method wants a source and a destination; the destination needs to be at least twice as large as the source.”
Consider using passive voice where necessary: “The inputs were all validated to be monotonic and even, so now ….”
DO NOT have more than one public type in a source file, unless they differ only in the number of generic parameters or one is nested in the other.
Multiple internal types in one file are allowed, though there is a preference for “one (top-level) type per file.”
DO name the source file with the name of the public type it contains.
For example, the String
class should be in a file named “String.cs” and the List<T>
class should be in a file named “List.cs”.
DO name the source file for a partial
type with the name of the primary file and a logical description of the contents of the file separated by a “.” (period), such as “JsonDocument.Parse.cs”.
DO organize the directory hierarchy just like the namespace hierarchy.
For example, the source file for System.Collections.Generic.List<T>
should be in the SystemCollectionsGeneric directory.
For assemblies that have a common namespace prefix for all types, removing empty top-level directories is a usual practice. Thus, a type named “SomeProject.BitManipulation.Simd” from an assembly with a common prefix of “SomeProject” would usually be found at <project root>BitManipulationSimd.cs.
DO group members into the following sections in the specified order:
All const fields
All static fields
All instance fields
All auto-implemented static properties
All auto-implemented instance properties
All constructors
Remaining members
All nested types
CONSIDER grouping the remaining members into the following sections in the specified order:
Public and protected properties
Methods
Events
All explicit interface implementations
Internal members
Private members
DO place the using directives outside the namespace declaration.
using System; namespace System.Collections { ... }
DO sort the using directives alphabetically, but place all System namespaces first.
using System; using System.Collections.Generic; using Microsoft.Win32.SafeHandles; namespace System.Diagnostics { ... }
3.21.162.87