Code style guidelines
Follow these code style guidelines whenever editing C# code of the compiler. Compliance with these guidelines is required for pull requests to be merged.
Naming conventions
- Use
_camelCaseprefixed with an underscore for private fields. - Use
camelCasefor method parameters and local variables. - Use
PascalCasefor all functions and public type members. - Use
PascalCasefor all types and type parameters. - Prefix all interface names with
I.
❌ Wrong:
public class worker
{
private string Something;
public void doWork(string _str)
{
Something = _str;
}
}
✔️ Correct:
public class Worker
{
private string _something;
public void DoWork(string str)
{
_something = str;
}
}
Namespaces
Always use file-scoped namespaces. Separate namespace declarations from using directives and type declarations with an empty line.
❌ Wrong:
namespace ExampleNamespace
{
class ExampleType
{
}
}
✔️ Correct:
namespace ExampleNamespace;
class ExampleType
{
}
Braces
Use Allman style braces.
❌ Wrong:
private void DoWork() {
// ...
}
✔️ Correct:
private void DoWork()
{
// ...
}
Access modifiers
Explicitly specify access modifiers on types and members, even if they are the default.
❌ Wrong:
class Worker
{
void DoWork()
{
// ...
}
}
✔️ Correct:
internal class Worker
{
private void DoWork()
{
// ...
}
}
Control flow statements
If the body of a control flow statement consists of only one line, omit the braces. Always use braces if the body is more than one line, even if it is just a single statement.
❌ Wrong:
if (something)
{
DoWork();
}
if (something)
obj = new()
{
Property = "Example"
};
✔️ Correct:
if (something)
DoWork();
if (something)
{
obj = new()
{
Property = "Example"
};
}
Object instantiation
Use target-typed new expressions wherever possible. Do not use var except for LINQ queries with complicated return types.
❌ Wrong:
StreamWriter writer = new StreamWriter("file.txt");
var writer = new StreamWriter("file.txt");
✔️ Correct:
StreamWriter writer = new("file.txt");
Collection initialization
Use C# 12 collection expressions.
❌ Wrong:
List<string> items = new();
int[] numbers = { 1, 2, 3 };
✔️ Correct:
List<string> items = [];
int[] numbers = [1, 2, 3];
LINQ
For most general cases, use method syntax. Avoid using query syntax unless strictly necessary for readability.
❌ Wrong:
int evenCount = (from n in numbers
where n % 2 == 0
select n).Count();
✔️ Correct:
int evenCount = numbers.Count(n => n % 2 == 0);
Expression-bodied members
Use expression-bodied methods and property accessors wherever possible.
❌ Wrong:
private string _name;
public string Name
{
get
{
return _name;
}
set
{
SetProperty(ref _name, value);
}
}
✔️ Correct:
private string _name;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
Documentation comments
Document all public types and their members with XML documentation comments.
❌ Wrong:
public interface ICompilerCommand
{
}
✔️ Correct:
/// <summary>
/// Defines a command used to add additional features to the Dassie compiler.
/// </summary>
public interface ICompilerCommand
{
}
String interpolation
Prefer string interpolation over string concatenation or String.Format.
❌ Wrong:
string message = "Error in file " + fileName + " at line " + lineNumber;
string formatted = String.Format("Error in file {0} at line {1}", fileName, lineNumber);
✔️ Correct:
string message = $"Error in file {fileName} at line {lineNumber}";
Null handling
Use null-conditional and null-coalescing operators where appropriate.
❌ Wrong:
if (config != null && config.Settings != null)
return config.Settings.Value;
return defaultValue;
✔️ Correct:
return config?.Settings?.Value ?? defaultValue;
Pattern matching
Use pattern matching for type checks and null checks.
❌ Wrong:
if (obj != null && obj is MyType)
{
MyType typed = (MyType)obj;
typed.DoSomething();
}
✔️ Correct:
if (obj is MyType typed)
typed.DoSomething();
File organization
Organize file contents in the following order:
- Using directives
- Namespace declaration
- Types (classes, interfaces, enums, etc.)
Within types, organize members in this order:
- Constants
- Fields
- Constructors
- Properties
- Methods
- Nested types
Error handling
Prefer specific exception types over generic ones. Include meaningful error messages.
❌ Wrong:
throw new Exception("Something went wrong");
✔️ Correct:
throw new InvalidOperationException($"Cannot process file '{fileName}': file not found.");
Async/Await
Use async/await for asynchronous operations. Suffix async method names with Async.
✔️ Correct:
public async Task<string> ReadFileAsync(string path)
{
return await File.ReadAllTextAsync(path);
}
Comments
- Use comments sparingly—code should be self-documenting where possible
- Write comments that explain why, not what
- Keep comments up to date with code changes
- Use
// TODO:for planned improvements - Use
// FIXME:for known issues
General principles
- Keep methods short: Methods should do one thing well. If a method exceeds ~30 lines, consider refactoring.
- Avoid magic numbers: Use named constants or enums instead of hardcoded values.
- Fail fast: Validate inputs early and throw meaningful exceptions.
- Prefer composition over inheritance: Use interfaces and composition for flexibility.
- Write testable code: Design components that can be easily unit tested.