My Personal C# Style Guide

The other day I was asked at work to write a style guide for our C# practice. So what is a developer to do? Googling it was a good place to start, but none of the ones I found quite fit. Some were 80 pages long and made me nauseous and others completely lacked any detail.

I settled on a modified corefx style guide. So, what do you think? I will be adding to this guide in the future and wanted to make sure I didn’t misplace it again. It will now live on in blog form, at least for now.

1 – Use Allman style braces, where each brace begins on a new line. A single line statement block can go without braces but the block must be properly indented on its own line and it must not be nested in other statement blocks that use braces.

2 – Use four spaces of indentation (no tabs).

3 – Use camelCase for internal and private fields and use readonly where possible. When used on static fields, readonly should come after static (i.e. static readonly not readonly static).

4 – Avoid this. unless absolutely necessary.

5 – Always specify the visibility, even if it’s the default (i.e. private string foo not string foo). Visibility should be the first modifier (i.e. public abstract not abstract public).

6 – Namespace imports should be specified at the top of the file, outside of namespace declarations and should be sorted alphabetically.

7 – Avoid more than one empty line at any time. For example, do not have two blank lines between members of a type.

8 – Avoid spurious free spaces. For example avoid if (someVar == 0)..., where the dots mark the spurious free spaces. Consider enabling “View White Space (Ctrl+E, S)” if using Visual Studio, to aid detection.

9 – Do not commit large blocks of commented out code. Use the source code repository for this feature. All comment out code in an actively edited file should be removed. Please do not go through the code base deleting commented out code and check it in.

10 – Do not create an interface unless there are two or more non-test scenario implementations that use that interface.

11 – Within a class, struct, or interface, elements should be positioned in the following order:

  • Constants
  • Fields
  • Constructors
  • Finalizers (Destructors)
  • Delegates
  • Events
  • Enums
  • Interfaces
  • Properties
  • Indexers
  • Methods
  • Structs
  • Classes

static elements have to appear before instance elements.

Elements should be ordered by access:

  • public
  • internal
  • protected internal
  • protected
  • private

12 – Avoid putting multiple top-level classes/interfaces/enums in the same file.

13 – Avoid the use of regions in code unless it is surrounding auto-generated code. Do not use regions to separate the different types of class members.

14 – Use var when it’s obvious what the variable type is (i.e. var stream = new FileStream(...) not var stream = OpenStandardInput()).

15 – We use language keywords instead of BCL types (i.e. int, string, float instead of Int32, String, Single, etc) for both type references as well as method calls (i.e. int.Parse instead of Int32.Parse). See issue 391 for examples.

16 – We use PascalCasing to name all our constant local variables and fields. The only exception is for interop code where the constant value should exactly match the name and value of the code you are calling via interop.

17 – We use nameof(...) instead of "..." whenever possible and relevant.

18 – When including non-ASCII characters in the source code use Unicode escape sequences (\uXXXX) instead of literal characters. Literal non-ASCII characters occasionally get garbled by a tool or editor.

19 – If a file happens to differ in style from these guidelines, update the file if you are actively working on that file.

Use the .NET Codeformatter Tool to ensure a code base maintains a consistent style over time, the tool automatically fixes the code base to conform to the guidelines outlined above.

Example File:

ObservableLinkedList.cs:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.Win32;

namespace System.Collections.Generic
{
    public partial class ObservableLinkedList : INotifyCollectionChanged, INotifyPropertyChanged
    {
        private ObservableLinkedListNode head;
        private int count;

        public ObservableLinkedList(IEnumerable items)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));

            foreach (T item in items)
            {
                AddLast(item);
            }
        }

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        public int Count
        {
            get { return count; }
        }

        public ObservableLinkedListNode AddLast(T value) 
        {
            var newNode = new LinkedListNode(this, value);

            InsertNodeBefore(head, node);
        }

        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler handler = CollectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }
}

This is a Visual Studio 2013 .vssettings file source for enabling C# auto-formatting conforming to the above guidelines. Note that rules 7 and 8 are not covered by the vssettings, since these are not rules currently supported by VS formatting.

<UserSettings>
    <ApplicationIdentity version="12.0"/>
    <ToolsOptions>
        <ToolsOptionsCategory name="TextEditor" RegisteredName="TextEditor">
            <ToolsOptionsSubCategory name="AllLanguages" RegisteredName="AllLanguages" PackageName="Text Management Package"/>
            <ToolsOptionsSubCategory name="CSharp" RegisteredName="CSharp" PackageName="Text Management Package">
                <PropertyValue name="TabSize">4</PropertyValue>
                <PropertyValue name="InsertTabs">false</PropertyValue>
                <PropertyValue name="IndentSize">4</PropertyValue>
                <PropertyValue name="BraceCompletion">true</PropertyValue>
            </ToolsOptionsSubCategory>
            <ToolsOptionsSubCategory name="CSharp-Specific" RegisteredName="CSharp-Specific" PackageName="Visual C# Language Service Package">
                <PropertyValue name="NewLines_QueryExpression_EachClause">1</PropertyValue>
                <PropertyValue name="Space_Normalize">0</PropertyValue>
                <PropertyValue name="Space_AroundBinaryOperator">1</PropertyValue>
                <PropertyValue name="Formatting_TriggerOnPaste">1</PropertyValue>
                <PropertyValue name="NewLines_Braces_Method">1</PropertyValue>
                <PropertyValue name="Indent_CaseLabels">1</PropertyValue>
                <PropertyValue name="Formatting_TriggerOnBlockCompletion">1</PropertyValue>
                <PropertyValue name="CodeDefinitionWindow_DocumentationComment_IndentOffset">2</PropertyValue>
                <PropertyValue name="NewLines_Braces_ControlFlow">1</PropertyValue>
                <PropertyValue name="NewLines_Braces_AnonymousMethod">0</PropertyValue>
                <PropertyValue name="Space_WithinOtherParentheses">0</PropertyValue>
                <PropertyValue name="Wrapping_KeepStatementsOnSingleLine">1</PropertyValue>
                <PropertyValue name="Space_AfterBasesColon">1</PropertyValue>
                <PropertyValue name="Indent_Braces">0</PropertyValue>
                <PropertyValue name="Wrapping_IgnoreSpacesAroundVariableDeclaration">0</PropertyValue>
                <PropertyValue name="Space_WithinMethodCallParentheses">0</PropertyValue>
                <PropertyValue name="Space_AfterCast">0</PropertyValue>
                <PropertyValue name="NewLines_Braces_CollectionInitializer">0</PropertyValue>
                <PropertyValue name="NewLines_AnonymousTypeInitializer_EachMember">1</PropertyValue>
                <PropertyValue name="NewLines_Keywords_Catch">1</PropertyValue>
                <PropertyValue name="NewLines_Braces_ObjectInitializer">0</PropertyValue>
                <PropertyValue name="NewLines_Braces_ArrayInitializer">0</PropertyValue>
                <PropertyValue name="Space_WithinExpressionParentheses">0</PropertyValue>
                <PropertyValue name="Space_InControlFlowConstruct">1</PropertyValue>
                <PropertyValue name="Formatting_TriggerOnStatementCompletion">0</PropertyValue>
                <PropertyValue name="NewLines_Keywords_Finally">1</PropertyValue>
                <PropertyValue name="Space_BetweenEmptyMethodDeclarationParentheses">0</PropertyValue>
                <PropertyValue name="Indent_UnindentLabels">0</PropertyValue>
                <PropertyValue name="NewLines_ObjectInitializer_EachMember">1</PropertyValue>
                <PropertyValue name="NewLines_Keywords_Else">1</PropertyValue>
                <PropertyValue name="Space_WithinMethodDeclarationParentheses">0</PropertyValue>
                <PropertyValue name="Space_BetweenEmptyMethodCallParentheses">0</PropertyValue>
                <PropertyValue name="Space_BeforeSemicolonsInForStatement">0</PropertyValue>
                <PropertyValue name="Space_BeforeComma">0</PropertyValue>
                <PropertyValue name="Space_AfterMethodCallName">0</PropertyValue>
                <PropertyValue name="Space_AfterComma">1</PropertyValue>
                <PropertyValue name="Wrapping_IgnoreSpacesAroundBinaryOperators">0</PropertyValue>
                <PropertyValue name="Space_BeforeBasesColon">1</PropertyValue>
                <PropertyValue name="Space_AfterMethodDeclarationName">0</PropertyValue>
                <PropertyValue name="Space_AfterDot">0</PropertyValue>
                <PropertyValue name="NewLines_Braces_Type">1</PropertyValue>
                <PropertyValue name="Space_AfterLambdaArrow">1</PropertyValue>
                <PropertyValue name="NewLines_Braces_LambdaExpressionBody">0</PropertyValue>
                <PropertyValue name="Space_WithinSquares">0</PropertyValue>
                <PropertyValue name="Space_BeforeLambdaArrow">1</PropertyValue>
                <PropertyValue name="NewLines_Braces_AnonymousTypeInitializer">0</PropertyValue>
                <PropertyValue name="Space_WithinCastParentheses">0</PropertyValue>
                <PropertyValue name="Space_AfterSemicolonsInForStatement">1</PropertyValue>
                <PropertyValue name="Indent_CaseContents">0</PropertyValue>
                <PropertyValue name="Indent_FlushLabelsLeft">1</PropertyValue>
                <PropertyValue name="Wrapping_PreserveSingleLine">1</PropertyValue>
                <PropertyValue name="Space_BetweenEmptySquares">0</PropertyValue>
                <PropertyValue name="Space_BeforeOpenSquare">0</PropertyValue>
                <PropertyValue name="Space_BeforeDot">0</PropertyValue>
                <PropertyValue name="Indent_BlockContents">1</PropertyValue>
                <PropertyValue name="SortUsings_PlaceSystemFirst">1</PropertyValue>
                <PropertyValue name="SortUsings">1</PropertyValue>
                <PropertyValue name="RemoveUnusedUsings">1</PropertyValue>
            </ToolsOptionsSubCategory>
        </ToolsOptionsCategory>
    </ToolsOptions>
</UserSettings>

One thought on “My Personal C# Style Guide

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s