C# Style Guide for 2sxc / EAV
Important: If you only want to USE 2sxc / EAV, then you do NOT need this. This is meant for people who want to contribute to the source code of 2sxc and EAV.
Concept
We want a consistent C# style across all 2sxc and EAV code, so that it is easy to read and maintain. This is why we have a C# Style Guide, which is based on the Microsoft C# Style Guide.
Tip
We highly recommend using ReSharper or Rider. They will recommend all of the style rules in this document, and it will also help you to automatically apply them.
In many cases you can use it for free on open source projects, or you can use the free Rider Community Edition.
Style Guide
- Use PascalCase for class names, method names, and property names.
- Use camelCase for local variables and method parameters.
- Use meaningful names for classes, methods, and properties.
- Use
var
when the type is obvious from the right-hand side of the assignment - even forout
parameters.- Do use explicit types when the type is not obvious or when it improves readability.
- Clean up after yourself: remove unused namespaces, variables, methods, and classes.
- Use
global using
for common global namespaces from core libraries such asToSic.Sys.DI
. - Use
async
andawait
for asynchronous programming.
2. Use Latest (Preview) C# Features
We want to use the latest C# features, so we can use the latest language features and improvements.
This means we will use the latest C# version available in the .NET SDK we are using.
By default, this is already enabled in all .csproj
files using <LangVersion>preview</LangVersion>
.
Important features to use are:
2.1 Use File Scoped Namespaces
namespace ToSic.Sxc.Example;
public class ExampleClass
{
public void ExampleMethod()
{
// Method implementation
}
}
2.2 Use field
Keyword and Init Setters
namespace ToSic.Sxc.Example;
public class ExampleClass
{
public string InitOnly { get; init; }
public string ExampleOne { get => field ??= "default value"; set; }
public string ExampleProperty
{
get => field ?? "fallback value";
set;
} = "default value";
}
2.4 Use []
for Collection Initializers
namespace ToSic.Sxc.Example;
public class ExampleClass
{
public List<string> ExampleList { get; set; } = [];
public Dictionary<string, string> ExampleDictionary { get; set; } = [];
public List<string> ExampleListWithItems { get; set; } = ["item1", "item2"];
// Using C# 12 Collection Expressions to Copy Lists
public List<string> ExampleListWithExpression { get; } = [..ExampleListWithItems, "item3", "item4"];
}
2.5 Use =>
for Simple Methods and Properties
-
Use
=>
for Simple Methods - Avoid Using Full Method Bodies for Simple Methods
-
Use Target Typed
new()
-
Avoid Using Full Type Names with
new
- Use Early Returns
-
Avoid Nested
if
Statements
namespace ToSic.Sxc.Example;
public class ExampleClass
{
public string ExampleMethod(string input) => $"Hello, {input}!";
public string ExampleProperty => $"Hello, {input}!";
}
3.2 Prefer Ternary Operators
namespace ToSic.Sxc.Example;
public class ExampleClass
{
public string ExampleMethod(string input)
{
return string.IsNullOrEmpty(input)
? "Input is empty"
: $"Hello, {input}!";
}
}
3.3 Use switch
Expressions
namespace ToSic.Sxc.Example;
public class ExampleClass
{
public string ExampleMethod(string input)
{
return input switch
{
null => "Input is null",
"" => "Input is empty",
_ => $"Hello, {input}!"
};
}
}
3.4 Use Ternary Throws
namespace ToSic.Sxc.Example;
public class ExampleClass
{
public string ExampleMethod(string input)
{
return input ?? throw new ArgumentException("Input cannot be null or empty", nameof(input))
}
public string ExampleMethodWithEmptyCheck(string input)
{
return string.IsNullOrEmpty(input) ? throw new ArgumentException("Input cannot be null or empty", nameof(input)) : input;
}
}
4. Use LINQ properly
LINQ is easy to learn and hard to master. Here are some tips to use LINQ properly:
- Use
Select
andWhere
to filter and transform collections. - Use
FirstOrDefault
andSingleOrDefault
to get single items from collections. - Use
Any
andAll
to check conditions on collections. - Use
Aggregate
to combine collections into a single value. - Use
ToList
andToArray
to convert collections to lists or arrays. - Use
AsEnumerable
to convert collections toIEnumerable<T>
before using LINQ methods. - Use
IEnumerable<T>
instead ofList<T>
orArray
when possible, to allow for more flexibility. - Use
IReadOnlyList<T>
,IReadOnlyCollection<T>
orIImmutableList<T>
for read-only collections.
Tip
It's usually better to use a ToList()
or ToArray()
at the end of a LINQ query, so that it is clear that the query is executed at that point.
This also helps to avoid performance issues with deferred execution, where the query is executed multiple times.
It also helps debug issues where they occur, and not later when the query is executed.
5. Use IServiceProvider
for Dependency Injection
We use IServiceProvider
for dependency injection, and register services in the Startup.cs
file.
This allows us to easily inject dependencies into classes and methods, and makes the code more testable and maintainable.
6. Use Proper Nullability Annotations
We use proper nullability annotations to indicate whether a parameter or return value can be null or not. This helps to avoid null reference exceptions and makes the code more readable.
Tip
As of v20, all the core libraries are now using nullable reference types.
This include ToSic.Sys
, ToSic.Sxc
, and ToSic.Eav
.
The Dnn / Oqtane libraries are not fully nullable yet, but will be in the future.
History
- New in 2023-09 v16.06
Shortlink: https://go.2sxc.org/build