C# Style Guide and Coding Conventions
February 24, 2026 ยท View on GitHub
Overview
In general, follow the Microsoft C# Coding Guidelines described in the following links:
- https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines
- https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/identifier-names
- https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions
Exceptions
The following guidelines supersede the Microsoft C# Coding Guidelines, and should be used.
Type Inference
Use type inference (var) wherever possible. This can improve readability and ease of refactoring.
ObjectResult exception
A Controller method with return type IActionResult, may return an OkObjectResult. If the method returns with
Ok(<something>), the type of <something> is specified in
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(<type>))]
but nowhere is that type enforced.
If <type> is (e.g.), string, considering using something like
string thingId = _service.Update(thing);
return Ok(thingId);
Using string thingId instead of var thingId is a guard in case the return type of Update changes.
Initiating empty collections
If the type is otherwise inferred, prefer [] over (e.g.) new List<string>() or new Dictionary<string, int>(), for
the sake of concision.
One-line if statements
Add braces to one-line if statements;
// Yes:
if (isEmpty)
{
callFun();
}
// No:
if (isEmpty)
callFun();
Rationale
Avoiding braces can cause developers to miss bugs, such as Apple's infamous goto-fail bug
Prefer Range for simple loop iteration
As an example, to loop 0, 1, 2, 3:
// Yes:
using static System.Linq.Enumerable;
foreach (var i in Range(0, 4))
// No:
for (var i = 0; i < 4; i++)
The signature of Range is:
Range (int start, int count);
Another example that loops 1, 2, 3:
// Yes:
using static System.Linq.Enumerable;
foreach (var i in Range(1, 3))
// No:
for (var i = 1; i < 4; i++)
Rationale
- Only need to mention loop variable (e.g.
i) once - Remove some error-prone boilerplate (
i++) - Remove the possibility of incrementing the wrong value (e.g. incrementing
iinstead ofjin an inner loop) - Express clearly the intent
Use IsNullOrEmpty to compare with empty string
var str = "Am I empty?";
// Yes
if (string.IsNullOrEmpty(str))
if (!string.IsNullOrEmpty(str))
// No
if (str == "")
if (str != string.Empty)
if (str.Equals(""))
Rationale
Uniform style, and still works if variable being checked becomes nullable.
NUnit testing
Prefer InstanceOf over TypeOf
Prefer asserts with Is.InstanceOf over Is.TypeOf to allow for inheritance, unless the specific type is needed.
Prefer AsyncMethod().Wait() over _ = AsyncMethod().Result
If you want to make sure an async method finishes but don't need what it returns, use .Wait() rather than assigning it
to an unused _ variable.
In async tests, prefer await over either of the above.
null handling
Prefer Assert.That(..., Is.Not.Null) over using ? or !.
var nullableObj = getObjOrNull();
// Yes
Assert.That(nullableObj, Is.Not.Null);
Assert.That(nullableObj.Id, Is.EqualTo(expectedId));
// No
Assert.That(nullableObj?.Id, Is.EqualTo(expectedId));
// No
Assert.That(nullableObj!.Id, Is.EqualTo(expectedId));
Rationale
Clear assert failure, rather than error, if thing is unexpected null.
Type casting nullable things
When type casting is necessary on something that might be null, use soft cast together with a non-null assertion.
// Yes
var nullableWord = getWordOrNull() as Word;
Assert.That(nullableWord, Is.Not.Null);
Assert.That(nullableWord.Id, Is.EqualTo(expectedId));
// No
var nullableWord = (Word)getWordOrNull();
Assert.That(nullableWord.Id, Is.EqualTo(expectedId));
// No
var nullableWord = getWordOrNull();
Assert.That(nullableWord, Is.InstanceOf<Word>());
Assert.That(((Word)nullableWord).Id, Is.EqualTo(expectedId));
Rationale
Clear assert failure if thing is unexpectedly null, and only have to specify the type once.