Messerli C# Coding Guidelines

These are the revised Coding Guidelines valid for code newly written in C#. If you see mistakes or have, comments do not hesitate in contacting the developer council.

When and where are these rules applicable

These rules are applicable to all C# code written at Messerli Informatik AG. The rules will not be enforced for old code, which has not been changed. In a review, these coding guidelines should be checked for all code, which has changed. Changes necessary due to the coding guidelines should be in a reasonable ratio to the functional changes.

Update to the rules

Updates to the rules should be sent to the developer council. The developer council will integrate new rules in a timely fashion when the council approves and will give feedback otherwise.

Definition of done

  • Feature is tested against acceptance criteria
  • Code adheres to the Coding Guidelines
  • Unit tests pass
  • Code is unit tested
  • Code is reviewed

Physical structure

Each class should go into its own file with the same name

Only one class should be in a file. This also applies to enumeration types (enum). Inner classes are allowed but discouraged.

Each namespace should go into its own folder with the same name

All classes that are held together by a common purpose should go into its own namespace; the namespace should be reflected in the folder structure. If you have a namespace Example, it should be in a folder example. Set a correct default namespace in the project, this way the tooling will help you to move classes into the correct location.

Each project should have a test project

We write tests in a separate project, therefore each project needs a test-project.

Formatting rules

We use Visual Studio, ReSharper and CodeMaid to enforce most of the formatting rules.

Tool configurations

The Formatting Rules for Visual Studio, ReSharper and CodeMaid are checked into the Repository, and can be found in the CTO git-Repository in the folder Tool Configurations:

  • .editorconfig
  • CodeMaid.config
  • MesserliResharper.DotSettings
  • VS_Format_Settings.vssettings

Indentation

For each scope that is opened, we indent by one level. For indentation, we use only spaces, tabs are forbidden. One level of indentation is four spaces.

Maximum line length

There is no absolute maximum to the length of the line, but try to keep it on the screen.

Empty lines

We use only one empty line to separate content.

Naming Conventions

Names should be descriptive; avoid abbreviations. Give a descriptive name, but be specific. Do not worry about saving horizontal space, as it is far more important to make your code immediately understandable by a new reader. We do not contract words or make up words.

We use English words for abstractions

  • The programming language is in English, most concepts are in English, therefore English is a lot easier to make consistent. (WriteExportAbacusEinFile, etc.)
  • Most of the concepts except a few in the building industry are already in English.
  • We want to avoid being confused about GetProject vs GetProjekt. For a single concept, only one word in one language should be chosen.
  • Avoid variables like workWork (sic)
  • It is a lot easier to create plurals in English (-s, -ies) where in German it might be difficult: Kapitel, Mitarbeiter, Unternehmer, Fenster, Artikel (see also next point)
  • Proper Nouns should not be translated even if possible.
  • We try to use the same word for the same abstraction. It is either a project or a tenant. (DDD)

Be specific

The world seen by an "object-oriented" programmer.
!DDD

Only use generic words like, data, list, string, number, manager, gateway and handler if necessary in a generic context. Otherwise, try to find specific domain words. Build a domain specific ubiquitous language. In domain context, always use the domain vocabulary and try keeping the overhead to a minimum, be precise.

Abbrevations

We do not use abbreviations that are ambiguous or unfamiliar to readers outside our project, and we do not abbreviate by skipping letters within a word. Abbreviations that would be familiar to someone outside your project with relevant domain knowledge are OK. As a rule of thumb, an abbreviation is probably OK if it is listed in Wikipedia like IP or HTML. All abbreviations are written in PascalCase.

class JsonToHtmlConverter
{
}
class IpAddress
{
}
class UserId
{
}
class TfsConnector
{
}

Abbrevation Examples

Use plural and singular to your advantage

If you have a collection of things, use the plural form of the variable you would use for a single element. Prefer being specific, as the name of the collection should reflect the meaning behind its elements. This gives you a natural understanding on what object you are dealing with.

var onlineUsers = GetOnlineUsers();
for (var user in onlineUsers) {
    user.GoOffline();
}

Correct usage of plural

Try to use the same word for the same concept

Do not switch between different name for a concept, make a choice and stick to it.

Naming the different C# identifiers

Table following table gives you the rules for each identifier.

IdentifierCasingPrefix / SuffixExample
NamespacePascalCaseMesserli.Core
ClassPascalCaseWarpEngine
Exception classPascalCase<Name>ExceptionInvalidArgumentException
InterfacePascalCaseI<Name>ISyntaxTree
Abstract classPascalCaseApplication
MethodPascalCaseDrawSquare
PropertiesPascalCaseFirstName
Predicate methodPascalCaseIs<Name>, Has<Name>, Are<Name>, Have<Name>IsGreat, HasField
Public Member VariablePascalCaseDiameter
Protected member variablecamelCase_<name>_tableIndex
Private member variablecamelCase_<name>_adjacencyMatrix
Local variablecamelCaseindex, name, helpLabel
Global constantPascalCasePi, PrimeNumbers
Class constantPascalCaseFilePath
Enum typePascalCaseStatusType
Enum valuePascalCaseRequiredValue
Lambda ParametrscamelCase or lower-case lettercornerPoint, c, name, n

UI Elements

When dealing with UI Elements like buttons, combo boxes, grids, text boxes, etc. we append the full name of the type without any prefix to the variable name.

class UserRightDialog
{
    Grid _userGrid;
    Edit _userNameEdit;
    Button _okButton;
};

Naming of UI Elements

Naming of delegate variables

All delegate variables should be in camelCase without a prefix or postfix. All other Methods are PascalCase which means if you see a createThing(), you will know that it is indeed a delegate call.

Naming of Tests

The naming of tests must describe what the intended effect of the methods that are being tested, e.g. ReturnsNullOnEmptySettings or ThrowsOnInvalidResponse.

Variables

Only one declaration per line

Multiple declarations per line are not allowed. This way, we can reduce the mental baggage of the reader and avoid awkward pointer and reference declarations (See the next subchapter). Deconstruction of Tuples and other types are obviously exempt from this rule.

var (name, address, city, zip) = contact.GetAddressInfo();

Example: tuple deconstruction

Local variables have good names

Local variables should have self-evident names too. Every variable should describe its content, and named consistently. One can often reuse the name of the type. Be as specific as necessary and as short as possible.

Declare variables in the innermost scope that is possible

This rule attempts to minimize the number of live variables that must be simultaneously considered. Furthermore, variable declarations should be postponed until enough information is available for full initialization. This rule also reduces the possibility of uninitialized and wrongly initialized variables.

Define variables on declaration

Local variables of primitive type are not initialized. Try to avoid separation of declaration and definition.

// Do not write this!
Money money;
money = GetMoney();

Bad example

Money money = GetMoney();

Better example

var money = GetMoney();

Even better example

This can lead to problems if your variable is initialized differently on a certain condition (if/switch). Prefer the use of if-expressions with the the ternary operator (? :) for simple cases or extract the initialization into a function

var money = HasBank() 
    ? GetFromBank() 
    : GetCash();

This could also be easily refactored into a function

This not only avoids uninitialized variables, but makes sure you always handle both cases.

With C# 8.0 switch-expressions are introduced which are highly recommended instead of switch-statements.

Do not reuse variables

A variable should have only one single purpose; there is no reason to recycle variables. The variables are abstractions for the human reader and not memory locations for the compiler.

Prefer type inference

Use var to avoid type names, especially those that are noisy, obvious, or unimportant — cases where the type does not aid in clarity for the reader. Only use manifest type declarations when it helps readability.

var textBox = GetTextBox();
textBox.SetText("foo");

Type inference

This does not mean that you should blindly replace types with var.

Do not use magic values

Every number except 0 or 1 needs a useful name derived from its true meaning in the code. However, this does not mean that 0 and 1 cannot have a name too. The same goes for magic strings!

Unused variables with discared values

Ideally we would prefer a clean way with pattern matching, where we always could use _. Since this is only possible in certain parts of the language, we still try to use the _ for unused variables. If you have more than one unused variable in the same context, use _0, _1, … instead.

Basic constructs

Only one expression per line

Multiple expressions or statements in one line increase the mental burden while reading them. Split them up to avoid mentally hiding statements.

If-statements

The condition of the if-statement should have no side effects. Avoid nested if-statements and prefer logical operators.

if (!Move(id))
{
    return false;
}

if (!UpdateStart(StartValue))
{
    return false;
}

Avoid side-effects

var itemFound = Move(id);
if (!itemFound)
{
    return false;
}

var updateIsReady = UpdateStart(StateDelete);
if (!updateIsReady)
{
    return false;
}

Better alternative

For-loops

Avoid for-loops and use LINQ and higher order functions as an alternative.

Prefer foreach-loops

Whenever you are iterating over some kind of enumerator, prefer the foreach syntax:

foreach (var value in values.OrderByDescending(v => v))
{
    // Do something in reverse order
}

foreach-loop

Switch-statement

Handle all cases

Unhandled cases lead the program into an undefined state. If you have no natural default case, declare one throwing an ArgumentException when dealing with an argument or an InvalidOperationException for all other cases.

Goto

The goto statement is forbidden in all cases.

Expressions

Prefer expressions over statements.

Prefer logical operators to if statements

if (node.GetParenthesis(key))
{
    item.Setting = false;
}
else
{
    item.Setting = true;
}

Avoid if and ternary operator for logical expressions

Logical expressions have no branches, are more concise and always handle all the logical cases.

item.Setting = !GetParenthesis(key);

Use logical operators !, && and ||

ternary operator

Prefer the ternary operator to simple if statements. The ternary operator is an expression in contrast to the if-statement and it forces you to handle all the cases.

We format the ternary operator like this, to see which branch we are dealing with easily.

return condition
    ? true-expression
    : false-expression;

ternary operator

Switch Expression

Prefer switch expressions over switch statements.

return animalKind switch
{
    AnimalKind.Dog => "dog",
    AnimalKind.Cat => "cat",
    _ => throw new InvalidOperationException($"Unsupported animal kind {animalKind}"),
};

switch expression

Expression-bodied Members

Use the expression body syntax when a member returns a single expression. Move the arrow to the next line when the expression gets too long.

public int Length => 0;

public string AbsolutePath()
    => Path.Combine(CalculateRootPath(), RelativePath);

expression-bodied members

Relational Patterns

We don't keep a space between the separator and the value in relational patterns.

string WaterState(int tempInFahrenheit)
    => tempInFahrenheit switch
    {
        >32 and <212 => "liquid",
        <32 => "solid",
        >212 => "gas",
        32 => "solid/liquid transition",
        212 => "liquid / gas transition",
    };

switch expression with relational patterns

Types

Use strong types

C# has a strong typing system, take advantage of it. A strong type will help you a lot in refactoring, and the compiler will easily tell you that you are using the wrong parameter if you do not have a string type like a comma separated list instead of a strong collection like a vector of longs.

public sealed record Vector(int X, int Y, int Z);

Vector CrossProduct(Vector factor1, Vector factor2);

// Never do this
bool CrossProduct(long x1, long y1, long z1, long x2, long y2, long z2,
                  out long resultX, out long resultY, out long resultZ);

Create a strong type

New type idiom

The idea behind the new type idiom is to have additional type information, even if you have a single integer as an argument. Even when we have simple integers, it usually represents a concept not as generic. This concept could be years, bytes, money or position. So even if you have a single integer, you can create a new type from your abstract concept.

public sealed record Years(int Value);

Declaring the new type Years

In this case we have a time period in years. The constructor should be explicit, otherwise a Year object would be automatically constructed from a given integer. That way you need to be explicit when you call a function like this:

bool OldEnough(Years years);

Use the new type

The Function OldEnough only accepts years. If you give an integer or a value of type Days you will get a compilation error. This helps in self-documentation and avoids misunderstandings between programmers with different assumptions. The code the compiler produces is the same with or without the additional type information. The additional Type-Information is just a compile time hint for the programmer.

Do not convert the types from the API if not necessary

Casting types can lead to unexpected behavior and should be avoided, if you need to convert types, chose a place where it minimizes the number of casts.

Define appropriate types

If two or more items of data belong together, try to find a name for them, and define a type you can reuse. Only use Tuple when the data is generic or part of an external interface.

Use appropriate data-structures

Do not pass around data structures as strings or tuples, when you could create a better type for your data.

Use Readonly PODs for data

All PODs must inject their state in the constructor and provide read only properties to it. A public setter is forbidden, a private setter is discouraged.

Make PODs easy to use

PODs should implement IEquatable or IComparable, prefer records over manual implementations.

Functional Programming

Immutability

While only data can have side effects, it is the methods and functions, which profit the most of immutability. They are much easier to understand and usually the code is much more elegant when you stay away from mutability. A function f in program P has no side-Effect if f() == f() for all states of P. In addition, the state of P does not change if you invoke f. Programs without side effects are easy to test because it implies that any test of f is independent of the state of P.

Prefer immutable types to mutable ones

An immutable type can only set its state through a constructor. No stateful setters are allowed on an immutable type. Changing state is done through transformation by copying. This way, even a setter can be written in an immutable way:

Use higher-order functions with LINQ and avoid writing your own for-loops

We try to avoid unnecessary repetition. For many things, there are generic algorithms. Use LINQ and other Algorithms to create powerful abstractions. If there is not an algorithm available, try to be generic and write your own algorithms.

TaskLINQ
ProjectionSelect, SelectMany
AggregationAggregate, Sum, Min, Max, Average, Count
RestrictionWhere
ExistenceAny, All, Contains, Distinct
Set OperationsContains, Distinct, Union, Intersect, Except
SortingOrderBy, ThenBy, OrderByDescending, ThenByDescending
PagingFirst, Last, Single, Skip, Take, SkipWhile, TakeWhile
String Formatingstring.Format() or use $"" (String interpolation)

LINQ syntax

Extension Methods and LINQ syntax are equivalent. Both have their up- and down-sides. Chose the one which you think is most appropriate for the Task at hand. You can also mix them in the same code file if it makes the code easier to read.

Use the import of static functions to your advantage

With the ability to import static functions, the syntax is much more natural to call functions on their own. Use that to your advantage and write free functions independent of any type.

Object oriented programming

Abstraction

Depend on abstractions not implementations

Classes which are not PODs must depend on abstractions and not implementations if possible.

Create your own abstractions if none are available

If the library you are using do not offer their own abstractions, create a facade with your own abstractions in front of the implementation.

Factories

Avoid the usage of new, either inject types through the constructor or use factories to create objects on runtime. All Factories should be declared via a delegate in the form of:

public delegate ReturnValue ReturnValueFactory(Parameters parameters);

Signature of factories

Inheritance

  • If the relation between two entities can be described as a "Entity A has B"-relation then composition is to be used. You should only use inheritance if you are dealing with a "Entity A is a kind of B"-relation. See: Composition over inheritance.
  • Prefer inheriting from interfaces
  • Keep your inheritance hierarchy flat
    • avoid abstract classes

Encapsulation

Data-Members are by default private

Classes are meant to hide the implementation details; this also means that your data should be hidden from the outside. If you really need public data-members, you might want to think about separating the data into a POD class.

Use the most restricitive visibility modifier possible

Information should be encapsulated as much as possible, which means in first instance it should be private.

public fields are forbidden

We only use properties for public interfaces for PODs and other classes.

Polymorphism

Use the strategy Pattern for different implementations of the same algorithm

The strategy pattern embodies two core principles of object-oriented programming, it encapsulates the concept that varies and lets programmers code to an interface, not an implementation.

Use the different forms of polymorphism to your advantage

C# supports runtime subtype polymorphism with virtual Methods, but there are other forms of polymorphism in C#.

The other form is the ability to overload methods with different parameter types which is a form of static compile time polymorphism. It is more powerful in the regard that you can overload on any type and you do not need to have a type hierarchy for it to work. However be careful on this, the overload resolution uses the static type-definition and not the run-type type definition. Which means this is limited to cases where you exactly know the type at compile time.

Use the visitor pattern for double dispatch

Static overload resolution can dispatch on more than one type, but at runtime we are limited to a single dispatch mechanism offered by the subtype polymorphism. To overcome this, we need to take advantage of both forms of polymorphism offered in C#. The visitor pattern is the right choice in such cases.

Algebraic Datatypes

Algebraic datatypes are also called discriminated unions, tagged unions, and sometimes "or types" colloquially. They can be found in many functional languages (even if they're not named algebraic datatypes specifically), and have become widely known and used in Typescript 2.0.

Algebraic datatypes in C#

While we don't have language support for algebraic datatypes in C#, there are some patterns on how you can implement something that behaves like one.

We recommend the usage of the following pattern using an abstract base class with a private constructor and inner, deriving classes and usually a match function. This has the advantage that the algebraic options are namespaced as the inner class, which simplifies the naming. The constructor is private to prevent extension of the algebraic datatype — with a private constructor only inner classes can derive from the abstract base class.

When your intention is to write something like an enum, but with attachable data to every option, algebraic datatypes is what you're looking for.

Example 1 (UpdateMode)

Our example has three different update modes - the ServerDefault, the Auto mode via ChannelName and a Version defined Pinned mode. Our match method allows us to define what should happen for each configured mode. The upside of that over a switch expression is that we have no problems with exhaustability (see Example 2).

public abstract class UpdateMode
{
    private UpdateMode()
    {
    }

    public abstract TResult Match<TResult>(
        Func<ServerDefault, TResult> serverDefault,
        Func<Auto, TResult> auto,
        Func<Pinned, TResult> pinned);

    public sealed class ServerDefault : UpdateMode
    {
        public override TResult Match<TResult>(
            Func<ServerDefault, TResult> serverDefault,
            Func<Auto, TResult> auto,
            Func<Pinned, TResult> pinned) 
            => serverDefault(this);
    }

    public sealed class Auto : UpdateMode
    {
        public Auto(string channelName)
        {
            ChannelName = channelName;
        }

        public string ChannelName { get; }

        public override TResult Match<TResult>(
            Func<ServerDefault, TResult> serverDefault,
            Func<Auto, TResult> auto,
            Func<Pinned, TResult> pinned) 
            => auto(this);
    }

    public sealed class Pinned : UpdateMode
    {
        public Pinned(string version)
        {
            Version = version;
        }

        public string Version { get; }

        public override TResult Match<TResult>(
            Func<ServerDefault, TResult> serverDefault,
            Func<Auto, TResult> auto,
            Func<Pinned, TResult> pinned) 
            => pinned(this);
    }
}

Summed up:

  • Abstract class (ensures inner classes are always used in a namespaced manner, e.g. UpdateMode.Auto)
  • Private constructor in abstract class (this ensures no one can derive from the abstract class except the inner classes)
  • An abstract match method with a Func<Variant, TResult> for every option of the algebraic datatype
  • Inner deriving sealed classes (that implement match and call the base constructor)
  • You can have data either on the base class and have it passed to the base constuctor, or in the deriving classes

Example 2 - why we recommend a match function

Consider the UpdateMode algebraic datatype from Example 1.

Usage example with Match:

var info = mode.Match(
    serverDefault => $"Server default mode",
    auto => $"Auto with channel {auto.ChannelName}",
    pinned => $"Pinned to Version {pinned.Version}");

Usage example with switch expression:

var info = mode switch
{
    UpdateMode.ServerDefault serverDefault => $"Server default mode",
    UpdateMode.Auto auto => $"Auto with channel {auto.ChannelName}",
    UpdateMode.Pinned pinned => $"Pinned to Version {pinned.Version}",
    _ => throw new Exception("Unreachable"), // we almost always need this, because the compiler doesn't know there's only 3 types
};

The advantage of using a match function is clear: No useless exception that is unreachable anyways, and immediate feedback from the compiler when adding another option to the algebraic datatype.

Use lables on the match method

It is good practice to use argument labels to avoid mixups in argument order, so consider this example superior to the Match example in Example 2:

var info = mode.Match(
    serverDefault: serverDefault => $"Server default mode",
    auto: auto => $"Auto with channel {auto.ChannelName}",
    pinned: pinned => $"Pinned to Version {pinned.Version}");

This is especially true if you don't need the argument itself and just execute an action:

// This example uses ActionToUnit and NoOperation from Funcky.
// void is not a valid generic argument for a method, so we often use the Unit type for some of these use cases.
// See the Funcky documentation (https://polyadic.github.io/funcky/) for more information on the Unit type.
static void SomeMethod() => NoOperation();
mode.Match(
    serverDefault: _ => ActionToUnit(SomeMethod),
    auto: _ => ActionToUnit(SomeMethod),
    pinned: _ => ActionToUnit(SomeMethod));

Disadvantages of using a match function, or why you might want your match function to be internal

When the match function is exposed from a library, adding a new variant to the algebraic datatype will break public api. Therefore, if you want to avoid this, you should make the match method internal. This makes you losing out on the exhaustiveness when the library is used, but at least you don't have to break public api to add another option.

Methods

Usually keep a method signature on one line

Alternativly get each parameter on a single line, especially for constructors where the injected objects can change often.

void Signature(int foo, string bar);

Function signature

Write short methods

Prefer small and focused functions. We recognize that long functions are sometimes appropriate, so no hard limit is placed on functions length. If a function exceeds about 30 lines, think about whether it can be broken up without harming the structure of the program. Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it easier for other people to read and modify your code. You could find long and complicated functions when working with some code. Do not be intimidated by modifying existing code: if working with such a function proves to be difficult, you find that errors are hard to debug, or you want to use a piece of it in several different contexts, consider breaking up the function into smaller and more manageable pieces.

Output parameters

Output parameters are forbidden. They are bad style and not necessary. Return multiple values as a tuple or your own better datastructure.

(ReceiverChannel, SendingChannel) CreateChannels();
var (rx, tx) = CreateChannels();

Multiple return values

Ref parameters

Ref parameters are forbidden in our own API.

Namespaces

Nothing should be in the global namespace.

  • The first level namespace is Messerli
  • The second level namespace reflects the module you are working in

Exceptions

Exceptions are a good tool to signal behavior out of the ordinary.

Use Nullable (?) types if you need to represent values with a no-value state

Do not use bools to indicate illegal state, as they can easily be ignored. Wrapping a returned type in an nullable value forces the caller to check for the state. Ideally use them in a monadic way with an appropriate library.

Do not use exceptions for control flow

Exceptions should be used to signal exceptional behavior, such as incorrect user input or missing resources. Using them for (expected) control flow is analogous to using a goto statement.

Miscellaneous

Use the Humble Object Pattern for System boundaries like UI

At the boundaries of the system, where things are often difficult to test use the humble object pattern to make it better testable. We accomplish the pattern by reducing the logic close to the boundary, making the code close to the boundary so humble that it doesn't need to be tested. The extracted logic is moved into another class, decoupled from the boundary which makes it testable.

Use the type system

Unit tests became extremely popular with dynamically typed languages like Ruby and JavaScript out of necessity. Those languages became popular initially for relatively small code fragments for automation or simple tasks. They were not designed as system languages with millions of lines of code in mind. But these languages became popular and the ecosystem grew and therefore also the need for refactoring in bigger projects. Since the type system were weak the compiler or run-time could not help much in finding defects. Unit tests do much more than just checking if the types are in the expected range, and if a function is correctly typed, but it is one kind of unit tests we do not need in statically typed languages. However it is important that you actually use the type System to your advantage.

Avoid regions

Regions can distract and hide things from you, that is why regions are generally discouraged except for automatically generated code, you wouldn't want to change by hand.

Comments

Though a pain to write, comments are vital to keeping our code readable. The following rules describe what you should comment and where. However, remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments. When writing your comments, write for your audience. The next contributor who will need to understand your code. Be generous - the next one may be you!

Language

Comments are written in English.

Style

We only use double and triple-slash comments. We do not use the /* */ - style comments. Write your comments above your code you want to comment. Only write comments on the right side of the code when doing so would benefit the readability greatly and the code in question is very short, e.g. an initialization. If your code describes a block of code, consider refactoring it into a function.

XML comments

Try to document your public interface.

  • The comments for an interface should be only in the header file.
  • Try avoiding stating the obvious.
  • We use XML-comments to document the interface.
/// <summary>
/// Specifies that <see cref="Open"/> should open an existing file.
/// When the file is opened, it should be truncated so that its size is zero bytes.
/// Requires that <see cref="Write"/> is called too.
/// Can not be used to together with <see cref="Append"/>.
/// </summary>
IFileOpeningBuilder Truncate(bool truncate = true);

Commenting a function

We want to adhere closely to this style because Visual Studio can read and interpret the XML comments, and IntelliSense will give you a real-time information on ctrl-space. As you can see, you do not only get the general summary, but a value-by-value reference of the parameters.

Write useful comments

A comment is useful, if it helps to understand the code better than without. Comments are not useful if they state the obvious or are repeating what the code already tells you. Comments are especially useful if you do something out of the ordinary, or complex. A high-level description of an algorithm for example is usually a good idea.

ASCII Art seperators are forbidden in code

For visual seperation you can use a #pragma region declaration.

A region can be folded for increased visibility:

Define a region
Region folded

Comments on curly brace

There are no comments on the ending curly brace. Your IDE can do code folding

Function not folded
Function folded

Do not commit code that is commented out

Code that is not compiled will rot and will be useless soon. If you really need the code, why is it not compiled? Delete the code. If you really need it again, you can always use your source control system.

Commit Message

We follow six of the seven rules of great commit messages

  1. Separate subject from body with a blank line
  2. Limit the subject line to 50 characters
  3. Capitalize the subject line
  4. Do not end the subject line with a period
  5. Use the imperative mood in the subject line
  6. Use the body to explain WHAT and WHY vs. HOW

The rule "Wrap the body at 72 characters" does not apply to TFVC users, as Visual Studio takes care of wrapping the body correctly.

In addition, we prepend the commit message with the module that has been worked on, followed by a colon. This prefix does not count towards the subject line character limit.

Language

Commit messages are written in English.

Bad example: Added feature
Good example: Document Editor: Add copy-pasting feature

Bad example: A position with a parts list can be inserted in your own parts list, which leads to a program crash.
Good example: Catalog: Fix Crash happening in recursive parts list ↵
A position which had itself a parts list could wrongly be inserted into a parts list.

Initializer

Index Initializers

Use index initializers with dictionary. This elegantly prevents uninitialized dictionaries. See Object and Collection Initializers (C# Programming Guide).

var dict = new Dictionary<string, int>
{
    ["key1"] = 1,
    ["key2"] = 50,
};

Good example

var dict = new Dictionary<string, int>();
dict["key1"] = 1;
dict["key2"] = 50;

Not so good example

var dict = new Dictionary<string, int>
{
    { "key1", 1 },
    { "key2", 50 },
};

Deprecated way example