C# Style Guide
The general rule we follow is "use Visual Studio defaults".
- We 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 must not be nested in other statement blocks that use braces (See RFC 18 for examples). One exception is that a
using
statement is permitted to be nested within anotherusing
statement by starting on the following line at the same indentation level, even if the nestedusing
contains a controlled block. - We use four spaces of indentation (no tabs).
- We use
_camelCase
for internal and private fields and usereadonly
where possible. Prefix internal and private instance fields with_
. When used on static fields,readonly
should come afterstatic
(e.g.static readonly
notreadonly static
). Public fields should be used sparingly and should use PascalCasing with no prefix when used. - We avoid
this.
unless absolutely necessary. - We always specify the visibility, even if it's the default (e.g.
private string _foo
notstring _foo
). Visibility should be the first modifier (e.g.public abstract
notabstract public
). - Namespace imports should be specified at the top of the file, outside of
namespace
declarations. They should be sorted with system namespaces alphabetically, then third party namespaces alphabetically. - Avoid more than one empty line at any time. For example, do not have two blank lines between members of a type.
- 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. - If a file happens to differ in style from these guidelines (e.g. private members are named
m_member
rather than_member
), the existing style in that file takes precedence. - The use of
var
is encouraged if it makes sense to do so. Good use cases are where the return type has a large name, where refactoring is possible. If there is a need for clarity than using the full type name is allowed. - We use language keywords instead of BCL types (e.g.
int, string, float
instead ofInt32, String, Single
, etc) for both type references as well as method calls (e.g.int.Parse
instead ofInt32.Parse
). See issue 391 for examples. - 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.
- We use
nameof(...)
instead of"..."
whenever possible and relevant. - Fields should be specified at the top within type declarations.
- 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.
- When using labels (for goto), indent the label one less than the current indentation.
- All publically exposed methods and properties should have a .net XML comment associated with them. This includes protected methods of public declared classes.
- Use of method groups is encourged where appropriate.
- Use the C# 7 style pattern matching when possible.
- When using methods with
out
parameters, try to use the inline variable feature within C# 7.
We have provided an analyzer ruleset (analyzers.ruleset
) at the root of the reactiveui repository, enabling C# auto-formatting conforming to the above guidelines.
Example File:
ObservableLinkedList`1.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<T> : INotifyCollectionChanged, INotifyPropertyChanged
{
private ObservableLinkedListNode<T> _head;
private int _count;
public ObservableLinkedList(IEnumerable<T> 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<T>(this, value);
InsertNodeBefore(_head, node);
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler handler = CollectionChanged;
if (handler != null)
{
handler(this, e);
}
}
private void InsertNodeBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
...
}
...
}
}
ObservableLinkedList`1.ObservableLinkedListNode.cs:
using System;
namespace System.Collections.Generics
{
partial class ObservableLinkedList<T>
{
public class ObservableLinkedListNode
{
private readonly ObservableLinkedList<T> _parent;
private readonly T _value;
internal ObservableLinkedListNode(ObservableLinkedList<T> parent, T value)
{
Debug.Assert(parent != null);
_parent = parent;
_value = value;
}
public T Value
{
get { return _value; }
}
}
...
}
}