Skip to content

Latest commit

 

History

History
183 lines (118 loc) · 4.35 KB

File metadata and controls

183 lines (118 loc) · 4.35 KB

C# Style Guide and Coding Conventions

Overview

In general, follow the Microsoft C# Coding Guidelines described in the following links:

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 i instead of j in 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.