Project coding standards¶
Git branch naming¶
- Branch names must be lowercase to avoid ref collisions on case-insensitive filesystems.
- Use only
a-z,0-9,.,_,/, and-characters. - Prefer slash-delimited prefixes, e.g.
feature/new-round-audio,fix/choco-pack-step.
.NET guidelines¶
- Use
slnxsolutions in favor ofsln.
C# guidelines¶
- All code is targeted at C# 13. The most recent syntax and techniques should always be preferred.
- All
usingstatements should beglobaland contained within toGlobalUsings.cs.- Global
usingaliases may be used to resolve problematic name collisions. - Namespaces should be systems in the the following order:
SystemMicrosoft- Other third-parties
GameShowPro- Local namespaces
- Global
- All code is in a nullable context.
- Nullable value types are used as appropriate.
- Use optional chaining
?.and nullish coalescing??operators. - Always use file-scoped namespaces.
- Prefer Linq implementations unless
- There is a clear performance penalty
- There is an edge case where there is a clearer or more terse way
- Non-trivial lambda functions should be defined as local functions instead.
- Functions that are only used by one parent function should always be a local function.
- Use simple using statements without braces where possible.
- Use collection expressions and collection literals where possible, e.g.:
ImmutableList<Widget> widgets = [.. list];instead of extensions methods likeImmutableList<Widget> widgets = list.ToImmutableList();.List<Widget> widgets = [];instead ofList<Widget> widgets = new();.
- Always prefer
RangeandIndexoperators for taking subsets of anIEnumerable<T>, e.g.:widgets[start..end]instead ofwidgets.GetRange(start, end - start).widgets[^1..]instead ofwidgets.GetRange(widgets.Count - 1, 1).
- Use a FrozenDictionary type for any dictionary that does not need to be changed after creation.
- Use ImmutableArray for any collection that does not need to be changed after creation.
- Use ImmutableList for any collection that may need to be changed after creation.
- Always declare variables as
readonlyif they will not be changed after initialization. - Use async whenever appropriate.
- Unless there is a synchronous overload, do not use the
Asyncsuffix for async methods. - "Fire and forget" async calls should always use the appropriately named utility class to make the intention clear. This should happen as far up the call stack as possible, minimizing the chance of calling a synchronous method without realizing it will trigger asynchronous work. All intermediate classes should be async.
- Never explicitly initialize module-level variables to a value that is already that type's default.
- E.g. do not use
private int _count = 0;orprivate string _name = "";. Instead, just useprivate int _count;orprivate string _name;.
- E.g. do not use
- Always use
nameofto refer to member names instead of hardcoding strings. - Do not use var under any circumstances. Always use explicit types.
- Use Target-typed new where ever possible.
- Never
var example = new Widget();. AlwaysWidget example = new();
- Never
- Use Target-typed new where ever possible.
- Always use the explicit discard (
_) character when ignoring the return oroutparameter of a method. - Primary constructors should be used unless there is a need to run logic during construction that can't be achieved without primary constructors.
- Parameters in primary constructors that will not be changed should be used in the class directly rather than assigning them to readonly fields.
- Address all code analyzer messages with codes using prefixes "IDE", "CA"," RCS", and "GSP". Use the Code Fix offered by the analyzer, if it has one.
C# Naming Conventions¶
- Static types are always prefixed with a
s_followed by camelCase. - Private class fields are always prefixed with
_followed by camelCase. - Methods, types, Constants, and property names always use PascalCase.
- Interface names always begin with
Ifollowed by PascalCase.
Razor page guidelines¶
- If the page subscribes to an event from an injected dependency, it should also implement
IAsyncDisposableand unsubscribe in theDisposeAsyncmethod. If no other cleanup is required, theDisposeAsyncmethod can finish by returningValueTask.CompletedTask;. - Services injected into the page should be named like any other private class field, i.e. prefixed with
_followed by camelCase.
Error Handling¶
- Use try/catch blocks for async operations