3.4 KiB
3.4 KiB
| description | globs | alwaysApply |
|---|---|---|
| C# naming, layout, and primary constructors; Microsoft conventions and .NET idioms. | **/*.cs | true |
C# style (Neon Sprawl)
Follow Microsoft’s C# Coding Conventions and C# identifier rules. Prefer clarity and consistency with existing server code.
Naming
- Types (classes, structs, records, interfaces, enums, delegates):
PascalCase. - Interfaces: prefix with
I(e.g.IPlayerSession). - Methods, properties, events, public fields:
PascalCase. - Parameters, local variables:
camelCase. - Private instance fields:
camelCase(no leading underscore), unless an existing file consistently does otherwise—then match the file. If a parameter or local shadows a field, usethis.or rename for clarity. - Static fields:
camelCasefor private/internal static fields;PascalCaseforpublic staticmembers (includingreadonly/constants) per Microsoft guidance; stay consistent within a project. - Async methods: suffix with
Async(e.g.LoadProfileAsync).
Primary constructors
- Prefer primary constructor syntax for classes, structs, and records when it fits: dependency injection,
IClassFixture<>test classes, small services, and any type that mainly captures parameters into fields or base calls. - Skip primary constructors when they hurt clarity: heavy logic in the body that belongs in a conventional constructor,
thisreferences before the implicit constructor runs in odd ways, or a file already uses a consistent legacy style—then match the file.
// Prefer
public sealed class OrderService(IOrderStore store, ILogger<OrderService> log)
{
public Task<Order?> GetAsync(Guid id) => store.FindAsync(id);
}
// Avoid when you would only reassign into mutable fields with non-trivial validation—use an explicit constructor instead.
Layout and syntax
- Braces: opening brace
{on a new line for types and members (Allman-style), per common Microsoft examples. - Indentation: 4 spaces per level; no tabs unless the file already uses tabs—never mix.
var: use when the type is obvious from the right-hand side; use explicit types when it improves readability or for literals where the type matters.- File-scoped namespaces (
namespace X;) for new single-namespace files when the SDK/version allows. - Pattern matching / nullability: prefer modern C# features where they simplify code; honor nullable reference type annotations when the project enables them.
Members
- Prefer expression-bodied members only when they stay one clear idea.
- LINQ: favor readability over micro-chains; break complex queries across lines.
- Exception handling: catch specific exceptions; avoid empty
catch; log or rethrow with context when appropriate.
Program.cs and minimal APIs
- Top-level statements and minimal APIs are fine for small apps; extract registration/build logic into extension methods or dedicated types when the file grows.
Documentation
- Use
///XML doc comments on public APIs (types and members) when behavior or contracts are not obvious.
Tooling
- Prefer fixes that satisfy built-in .NET analyzers /
dotnet format(if adopted) rather than fighting IDE warnings without reason.