A

C# Coding Style Conventions

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.

A.1 General Style Conventions

A.1.1 Brace Usage

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.

Images DO place the opening brace on the next line, indented to the same level as the block statement.

if (someExpression)
{
    DoSomething();
}

Images DO align the closing brace with the opening brace.

if (someExpression)
{
    DoSomething();
}

Images 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)
{
    ...
}

Images 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;

Images 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 ifthrow, 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.

Click here to view code image

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;
    }

    ...
}

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

Click here to view code image

Right:
using (Element element = GetElement())
{
    ...
}

Wrong:
using Element element = GetElement();
...

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

Click here to view code image

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);
...

A.1.2 Space Usage

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

Click here to view code image

public int Foo { get { return foo; } }

Images DO use a single space after a comma between parameters.

Click here to view code image

Right: public void Foo(char bar, int x, int y)
Wrong: public void Foo(char bar,int x,int y)

Images DO use a single space between arguments.

Right: Foo(myChar, ʘ, 1)
Wrong: Foo(myChar,ʘ,1)

Images DO NOT use spaces after the opening parenthesis or before the closing parenthesis.

Right: Foo(myChar, ʘ, 1)
Wrong: Foo( myChar, ʘ, 1 )

Images DO NOT use spaces between a member name and an opening parenthesis.

Right: Foo()
Wrong: Foo ()

Images DO NOT use spaces after or before square braces.

Click here to view code image

Right: x = dataArray[index];
Wrong: x = dataArray[ index ];

Images DO use spaces between flow control keywords and the open parenthesis.

Right: while (x == y)
Wrong: while(x == y)

Images DO use spaces before and after binary operators.

Right: if (x == y) { ... }
Wrong: if (x==y) { ... }

Images DO NOT use spaces before or after unary operators.

Right: if (!y){ ... }
Wrong: if (! y){ ... }

A.1.3 Indent Usage

Images DO use four consecutive space characters for indents.

Images DO NOT use the tab character for indents.

Images DO indent contents of code blocks.

if (someExpression)
{
    DoSomething();
}

Images DO indent case blocks even if not using braces.

switch (someExpression)
{
    case ʘ:
        DoSomething();
        break;
    ...
}

Images DO “outdent” goto labels one indentation level.

private void SomeMethod()
{
    int iteration = ʘ;

start:
    ...

    if (...)
    {
    nested_label:
        iteration++;
        goto start;
    }

    ...
}

Images DO indent all continued lines of a single statement one indentation level.

Click here to view code image

bool firstResult = list.Count > ʘ ?
    list[list.Length – 1} > list[ʘ] :
    list.Capacity > 1ʘʘʘ;

string complex =
    _table[index].Property.Method(methodParameter).
        Method2(method2Parameter, method2Parameter2).
        Property2;

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

Click here to view code image

private void SomeMethod(
    int firstParameter,
    string secondParameter,
    bool thirdParameter,
    string fourthParameter)
{

    ...

    SomeOtherMethod(
        firstParameter * 1ʘʘ,
        fourthParameter,
        thirdParameter &&
            secondParameter.Length > fourthParameter.Length,
        22);

    ...
}

A.1.4 Vertical Whitespace

Images DO add a blank line before control flow statements.

Images DO add a blank line after a closing brace, unless the next line is also a closing brace.

Click here to view code image

while (!queue.IsEmpty)
{
    Element toProcess = queue.Pop();

    if (toProcess == null)
    {
        continue;
    }

    if (!toProcess.IsDone)
    {
        ...
    }
}

Images DO add a blank line after “paragraphs” of code where it enhances readability.

switch (someExpression)
{
    case ʘxs:
        DoSomething();
        break;
    ...
}

A.1.5 Member Modifiers

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.

Images DO always explicitly specify visibility modifiers.

Click here to view code image

Right:
    internal class SomeClass
    {
        private object _lockObject;
        ...
    }

Wrong:
    class SomeClass
    {
        object _lockObject;
        ...
    }

Images DO specify the visibility modifier as the first modifier.

Click here to view code image

Right: protected abstract void Reset();
Wrong: abstract protected void Reset();

Images DO specify the static modifier immediately after visibility, for static members or static classes.

Click here to view code image

Right: private static readonly ConcurrentQueue<T> ...
Wrong: private readonly static ConcurrentQueue<T> ...

Images DO specify the extern method modifier immediately after the static modifier for an extern method.

Click here to view code image

Right: private static extern IntPtr CreateFile(...);
Wrong: private extern static IntPtr CreateFile(...);

Images DO specify the member slot modifier (new, virtual, abstract, sealed, or override) immediately after the static modifier (if specified).

Click here to view code image

Right: public static new SomeType Create(...)
Wrong: public new static SomeType Create(...)

Images DO specify the readonly modifier for fields or methods immediately after the member slot modifier (if specified).

Click here to view code image

Right: private new readonly object _lockObject = new object();
Wrong: private readonly new object _lockObject = new object();

Images DO specify the unsafe modifier for methods immediately after the readonly modifier (if specified).

Click here to view code image

Right: public readonly unsafe Matrix Multiply(...)
Wrong: public unsafe readonly Matrix Multiply(...)

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

Click here to view code image

Right: private static volatile int s_instanceCount;
Wrong: private volatile static int s_instanceCount;

Images DO specify the async modifier as the last method modifier, when used.

Click here to view code image

Right: public readonly async Task CloseAsync(...)
Wrong: public async readonly Task CloseAsync(...)

Images DO use “protected internal” instead of “internal protected” for a member that can be called by derived types or types in the same assembly.

Click here to view code image

Right: protected internal void Reset()
Wrong: internal protected void Reset()

Images DO use “private protected” instead of “protected private” for a member that can be called by derived types within the same assembly.

Click here to view code image

Right: private protected void Reset()
Wrong: protected private void Reset()

A.1.6 Other

Images DO add the optional trailing comma after the last enum member.

Images 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
    }

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

Images DO use language keywords (string, int, double, …) instead of BCL type names (String, Int32, Double, …), for both variable declarations and method invocation.

Click here to view code image

Right:
    string[] split = string.Split(message, Delimiters);
    int count = split.Length;

Wrong:
    String split = String.Split(message, Delimiters);
    Int32 count = split.Length;

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

Click here to view code image

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)
    {
        ...
    }

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

Click here to view code image

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 = ...;

Images DO use collection initializers where possible.

Click here to view code image

Right:
     SomeCollection someColl = new SomeCollection
     {
         first,
         second,
         third,
     };

Not preferred:
    SomeCollection someColl = new SomeCollection();
    someColl.Add(first);
    someColl.Add(second);
    someColl.Add(third);

Images CONSIDER using expression-bodied members when the implementation of a property or method is unlikely to change.

Click here to view code image

public partial class UnseekableStream : Stream
{
    public bool CanSeek => false;
    public override long Seek(long offset, SeekOrigin origin) =>
         throw new NotSupportedException();
}

Images DO use auto-implemented properties when the implementation is unlikely to change.

Click here to view code image

internal partial class SomeCache
{
    internal long HitCount { get; private set; }
    internal long MissCount { get; private set; }
}

Images 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”.

Click here to view code image

public partial class RuʘʘE9sumuʘʘE9
{
     ...
}

...
string nihongo = "u65E5u672Cu8A9E";

Images DO use the nameof(...) syntax, instead of a literal string, when referring to the name of a type, member, or parameter.

Click here to view code image

Right: throw new ArgumentNullException(nameof(target));
Wrong: throw new ArgumentNullException("target");

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

Images DO use ifthrow 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 ifthrow generalizes without exception to types with and without finalizers, as well as to both methods and constructors.

Click here to view code image

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));

    ...
}

A.2 Naming Conventions

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.

Images DO follow the Framework Design Guidelines for naming identifiers, except for naming private and internal fields.

Images DO use PascalCasing for namespace, type, and member names, except for internal and private fields.

Click here to view code image

namespace Your.MultiWord.Namespace
{
    public abstract class SomeClass
    {
        internal int SomeProperty { get; private set; }
        ...
    }
}

Images DO use PascalCasing for const locals and const fields except for interop code, where it should match the name from the called code exactly.

Click here to view code image

internal class SomeClass
{
    private const DefaultListSize = 32;

    private bool Encode()
    {
        const int RetryCount = 3;
        const int ERROR_MORE_DATA = ʘxEA;

        ...
    }
}

Images DO use camelCasing for private and internal fields.

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

Click here to view code image

internal partial class SomeClass
{
    [ThreadStatic]
    private static byte[] t_buffer;

    private static int s_instanceCount;

    private int _instanceId;
}

Images DO use camelCasing for local variables.

Images DO use camelCasing for parameters.

Images DO NOT use Hungarian notation (i.e., do not encode the type of a variable in its name).

A.3 Comments

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.

Images DO NOT use comments unless they describe something not obvious to someone other than the developer who wrote the code.

Images AVOID multiline syntax (/* ... */) for comments. The single-line syntax (// ...) is preferred even when a comment spans multiple lines.

Click here to view code image

// 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
{
    ...
}

Images DO NOT place comments at the end of a line unless the comment is very short.

Images AVOID placing comments at the end of a line even when the comment is very short.

Click here to view code image

//Avoid
public class ArrayList
{
    private int count; // -1 indicates uninitialized array
}

Images 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 ….”

A.4 File Organization

Images 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.”

Images 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”.

Images 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”.

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

Images 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

Images 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

Images DO place the using directives outside the namespace declaration.

using System;

namespace System.Collections
{
    ...
}

Images DO sort the using directives alphabetically, but place all System namespaces first.

Click here to view code image

using System;
using System.Collections.Generic;
using Microsoft.Win32.SafeHandles;

namespace System.Diagnostics
{
    ...
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.162.87