dotnet-csharp-coding-standards
Modern .NET coding standards based on Microsoft Framework Design Guidelines and C# Coding Conventions. This skill covers
naming, file organization, and code style rules that agents should follow when generating or reviewing C# code.
Activation Guidance
Load this skill by default for any task that plans, designs, generates, modifies, or reviews C#/.NET code. Do not wait
for explicit user wording such as "coding standards", "style", or "conventions". If code will be produced, this skill
should be active before implementation starts. This skill is a baseline dependency that should be loaded before
domain-specific C#/.NET skills.
Cross-references: [skill:dotnet-csharp-modern-patterns] for language feature usage, [skill:dotnet-csharp-async-patterns]
for async naming conventions, [skill:dotnet-solid-principles] for SOLID, DRY, and SRP design principles at the class and
interface level.
Scope
- Naming conventions (PascalCase, camelCase, I-prefix for interfaces)
- File organization and namespace conventions
- Code style rules (expression bodies, using directives, var usage)
- EditorConfig integration for style enforcement
Out of scope
- Language feature patterns (records, pattern matching) -- see [skill:dotnet-csharp-modern-patterns]
- Async naming and await conventions -- see [skill:dotnet-csharp-async-patterns]
- SOLID/DRY design principles -- see [skill:dotnet-solid-principles]
- Code smells and anti-patterns -- see [skill:dotnet-csharp-code-smells]
Naming Conventions
General Rules
| Element |
Convention |
Example |
| Namespaces |
PascalCase, dot-separated |
MyCompany.MyProduct.Core |
| Classes, Records, Structs |
PascalCase |
OrderService, OrderSummary |
| Interfaces |
I + PascalCase |
IOrderRepository |
| Methods |
PascalCase |
GetOrderAsync |
| Properties |
PascalCase |
OrderDate |
| Events |
PascalCase |
OrderCompleted |
| Public constants |
PascalCase |
MaxRetryCount |
| Private fields |
_camelCase |
_orderRepository |
| Parameters, locals |
camelCase |
orderId, totalAmount |
| Type parameters |
T or T + PascalCase |
T, TKey, TValue |
| Enum members |
PascalCase |
OrderStatus.Pending |
Async Method Naming
Suffix async methods with Async:
// Correct
public Task<Order> GetOrderAsync(int id);
public ValueTask SaveChangesAsync(CancellationToken ct);
// Wrong
public Task<Order> GetOrder(int id); // missing Async suffix
public Task<Order> GetOrderTask(int id); // wrong suffix
```text
Exception: Event handlers and interface implementations where the framework does not use the `Async` suffix (e.g.,
ASP.NET Core middleware `InvokeAsync` is already named by the framework).
### Boolean Naming
Prefix booleans with `is`, `has`, `can`, `should`, or similar:
```csharp
public bool IsActive { get; set; }
public bool HasOrders { get; }
public bool CanDelete(Order order);
```csharp
### Collection Naming
Use plural nouns for collections:
```csharp
public IReadOnlyList<Order> Orders { get; } // not OrderList
public Dictionary<string, int> CountsByName { get; } // descriptive
```csharp
---
## File Organization
### One Type Per File
Each top-level type (class, record, struct, interface, enum) should be in its own file, named exactly as the type.
Nested types stay in the containing type's file.
```text
OrderService.cs -> public class OrderService
IOrderRepository.cs -> public interface IOrderRepository
OrderStatus.cs -> public enum OrderStatus
OrderSummary.cs -> public record OrderSummary
```csharp
### File-Scoped Namespaces
Always use file-scoped namespaces (C# 10+):
```csharp
// Correct
namespace MyApp.Services;
public class OrderService { }
// Avoid: block-scoped namespace adds unnecessary indentation
namespace MyApp.Services
{
public class OrderService { }
}
```text
### Using Directives
Place `using` directives at the top of the file, outside the namespace. With `<ImplicitUsings>enable</ImplicitUsings>`
(default in modern .NET), common namespaces are already imported. Only add explicit `using` statements for namespaces
not covered by implicit usings.
Order of `using` directives:
1. `System.*` namespaces
2. Third-party namespaces
3. Project namespaces
### Directory Structure
Organize by feature or layer, matching namespace hierarchy:
```text
src/MyApp/
Features/
Orders/
OrderService.cs
IOrderRepository.cs
OrderEndpoints.cs
Users/
UserService.cs
Infrastructure/
Persistence/
OrderRepository.cs
```csharp
---
## Code Style
### Braces
Always use braces for control flow, even for single-line bodies:
```csharp
// Correct
if (order.IsValid)
{
Process(order);
}
// Avoid
if (order.IsValid)
Process(order);
```text
### Expression-Bodied Members
Use expression bodies for single-expression members:
```csharp
// Properties
public string FullName => $"{FirstName} {LastName}";
// Methods (single expression only)
public override string ToString() => $"Order #{Id}";
// Avoid for multi-statement methods -- use block body instead
```text
### `var` Usage
Use `var` when the type is obvious from the right-hand side:
```csharp
// Type is obvious: use var
var orders = new List<Order>();
var customer = GetCustomerById(id);
var name = "Alice";
// Type is not obvious: use explicit type
IOrderRepository repo = serviceProvider.GetRequiredService<IOrderRepository>();
decimal total = CalculateTotal(items);
```text
### Null Handling
Prefer pattern matching over null checks:
```csharp
// Preferred
if (order is not null) { }
if (order is { Status: OrderStatus.Active }) { }
// Acceptable
if (order != null) { }
// Avoid
if (order is object) { }
if (!(order is null)) { }
```text
Use null-conditional and null-coalescing operators:
```csharp
var name = customer?.Name ?? "Unknown";
var orders = customer?.Orders ?? [];
items ??= [];
```csharp
### String Handling
Prefer string interpolation over concatenation or `string.Format`:
```csharp
// Preferred
var message = $"Order {orderId} totals {total:C2}";
// For complex interpolations, use raw string literals (C# 11+)
var json = $$"""
{
"id": {{orderId}},
"name": "{{name}}"
}
""";
// Avoid
var message = string.Format("Order {0} totals {1:C2}", orderId, total);
var message = "Order " + orderId + " totals " + total.ToString("C2");
```text
---
## Access Modifiers
Always specify access modifiers explicitly. Do not rely on defaults:
```csharp
// Correct
public class OrderService
{
private readonly IOrderRepository _repo;
internal void ProcessBatch() { }
}
// Avoid: implicit internal class, implicit private field
class OrderService
{
readonly IOrderRepository _repo;
}
```text
### Modifier Order
Follow the standard order:
```text
access (public/private/protected/internal) -> static -> extern -> new ->
virtual/abstract/override/sealed -> readonly -> volatile -> async -> partial
```text
```csharp
public static readonly int MaxSize = 100;
protected virtual async Task<Order> LoadAsync() => await repo.GetDefaultAsync();
public sealed override string ToString() => Name;
```csharp
---
## Type Design
These conventions implement SOLID and DRY principles at the code level. For comprehensive coverage with anti-patterns
and fixes, see [skill:dotnet-solid-principles].
### Seal Classes by Default
Seal classes that are not designed for inheritance. This improves performance (devirtualization) and communicates
intent:
```csharp
public sealed class OrderService(IOrderRepository repo)
{
// Not designed for inheritance
}
```text
Only leave classes unsealed when you explicitly design them as base classes.
### Prefer Composition Over Inheritance
```csharp
// Preferred: composition
public sealed class OrderProcessor(IValidator validator, INotifier notifier)
{
public async Task ProcessAsync(Order order)
{
await validator.ValidateAsync(order);
await notifier.NotifyAsync(order);
}
}
// Avoid: deep inheritance hierarchies
public class BaseProcessor { }
public class ValidatingProcessor : BaseProcessor { }
public class NotifyingValidatingProcessor : ValidatingProcessor { }
```text
### Interface Segregation
Keep interfaces focused. Prefer multiple small interfaces over one large one:
```csharp
// Preferred
public interface IOrderReader
{
Task<Order?> GetByIdAsync(int id, CancellationToken ct = default);
Task<IReadOnlyList<Order>> GetAllAsync(CancellationToken ct = default);
}
public interface IOrderWriter
{
## Detailed Examples
See [references/detailed-examples.md](references/detailed-examples.md) for complete code samples and advanced patterns.
1---2name: dotnet-csharp-coding-standards3description: Defines baseline C# conventions loaded first -- naming, file layout, style rules.4license: MIT5targets: ['*']6category: fundamentals7subcategory: coding-standards8tags:9 - csharp10 - dotnet11 - skill12 - coding-standards13 - naming14version: '1.0.0'15author: 'dotnet-agent-harness'16invocable: true17related_skills:18 - dotnet-csharp-modern-patterns19 - dotnet-csharp-nullable-reference-types20 - dotnet-editorconfig21claudecode:22 allowed-tools: ['Read', 'Grep', 'Glob', 'Bash', 'Write', 'Edit']23codexcli:24 short-description: '.NET skill guidance for csharp tasks'25opencode:26 allowed-tools: ['Read', 'Grep', 'Glob', 'Bash', 'Write', 'Edit']27copilot: {}28geminicli: {}29antigravity: {}30---3132# dotnet-csharp-coding-standards3334Modern .NET coding standards based on Microsoft Framework Design Guidelines and C# Coding Conventions. This skill covers35naming, file organization, and code style rules that agents should follow when generating or reviewing C# code.3637## Activation Guidance3839Load this skill by default for any task that plans, designs, generates, modifies, or reviews C#/.NET code. Do not wait40for explicit user wording such as "coding standards", "style", or "conventions". If code will be produced, this skill41should be active before implementation starts. This skill is a baseline dependency that should be loaded before42domain-specific C#/.NET skills.4344Cross-references: [skill:dotnet-csharp-modern-patterns] for language feature usage, [skill:dotnet-csharp-async-patterns]45for async naming conventions, [skill:dotnet-solid-principles] for SOLID, DRY, and SRP design principles at the class and46interface level.4748## Scope4950- Naming conventions (PascalCase, camelCase, I-prefix for interfaces)51- File organization and namespace conventions52- Code style rules (expression bodies, using directives, var usage)53- EditorConfig integration for style enforcement5455## Out of scope5657- Language feature patterns (records, pattern matching) -- see [skill:dotnet-csharp-modern-patterns]58- Async naming and await conventions -- see [skill:dotnet-csharp-async-patterns]59- SOLID/DRY design principles -- see [skill:dotnet-solid-principles]60- Code smells and anti-patterns -- see [skill:dotnet-csharp-code-smells]6162---6364## Naming Conventions6566### General Rules6768| Element | Convention | Example |69| ------------------------- | ------------------------- | ------------------------------ |70| Namespaces | PascalCase, dot-separated | `MyCompany.MyProduct.Core` |71| Classes, Records, Structs | PascalCase | `OrderService`, `OrderSummary` |72| Interfaces | `I` + PascalCase | `IOrderRepository` |73| Methods | PascalCase | `GetOrderAsync` |74| Properties | PascalCase | `OrderDate` |75| Events | PascalCase | `OrderCompleted` |76| Public constants | PascalCase | `MaxRetryCount` |77| Private fields | `_camelCase` | `_orderRepository` |78| Parameters, locals | camelCase | `orderId`, `totalAmount` |79| Type parameters | `T` or `T` + PascalCase | `T`, `TKey`, `TValue` |80| Enum members | PascalCase | `OrderStatus.Pending` |8182### Async Method Naming8384Suffix async methods with `Async`:8586````csharp8788// Correct89public Task<Order> GetOrderAsync(int id);90public ValueTask SaveChangesAsync(CancellationToken ct);9192// Wrong93public Task<Order> GetOrder(int id); // missing Async suffix94public Task<Order> GetOrderTask(int id); // wrong suffix9596```text9798Exception: Event handlers and interface implementations where the framework does not use the `Async` suffix (e.g.,99ASP.NET Core middleware `InvokeAsync` is already named by the framework).100101### Boolean Naming102103Prefix booleans with `is`, `has`, `can`, `should`, or similar:104105```csharp106107public bool IsActive { get; set; }108public bool HasOrders { get; }109public bool CanDelete(Order order);110111```csharp112113### Collection Naming114115Use plural nouns for collections:116117```csharp118119public IReadOnlyList<Order> Orders { get; } // not OrderList120public Dictionary<string, int> CountsByName { get; } // descriptive121122```csharp123124---125126## File Organization127128### One Type Per File129130Each top-level type (class, record, struct, interface, enum) should be in its own file, named exactly as the type.131Nested types stay in the containing type's file.132133```text134135OrderService.cs -> public class OrderService136IOrderRepository.cs -> public interface IOrderRepository137OrderStatus.cs -> public enum OrderStatus138OrderSummary.cs -> public record OrderSummary139140```csharp141142### File-Scoped Namespaces143144Always use file-scoped namespaces (C# 10+):145146```csharp147148// Correct149namespace MyApp.Services;150151public class OrderService { }152153// Avoid: block-scoped namespace adds unnecessary indentation154namespace MyApp.Services155{156 public class OrderService { }157}158159```text160161### Using Directives162163Place `using` directives at the top of the file, outside the namespace. With `<ImplicitUsings>enable</ImplicitUsings>`164(default in modern .NET), common namespaces are already imported. Only add explicit `using` statements for namespaces165not covered by implicit usings.166167Order of `using` directives:1681691. `System.*` namespaces1702. Third-party namespaces1713. Project namespaces172173### Directory Structure174175Organize by feature or layer, matching namespace hierarchy:176177```text178179src/MyApp/180 Features/181 Orders/182 OrderService.cs183 IOrderRepository.cs184 OrderEndpoints.cs185 Users/186 UserService.cs187 Infrastructure/188 Persistence/189 OrderRepository.cs190191```csharp192193---194195## Code Style196197### Braces198199Always use braces for control flow, even for single-line bodies:200201```csharp202203// Correct204if (order.IsValid)205{206 Process(order);207}208209// Avoid210if (order.IsValid)211 Process(order);212213```text214215### Expression-Bodied Members216217Use expression bodies for single-expression members:218219```csharp220221// Properties222public string FullName => $"{FirstName} {LastName}";223224// Methods (single expression only)225public override string ToString() => $"Order #{Id}";226227// Avoid for multi-statement methods -- use block body instead228229```text230231### `var` Usage232233Use `var` when the type is obvious from the right-hand side:234235```csharp236237// Type is obvious: use var238var orders = new List<Order>();239var customer = GetCustomerById(id);240var name = "Alice";241242// Type is not obvious: use explicit type243IOrderRepository repo = serviceProvider.GetRequiredService<IOrderRepository>();244decimal total = CalculateTotal(items);245246```text247248### Null Handling249250Prefer pattern matching over null checks:251252```csharp253254// Preferred255if (order is not null) { }256if (order is { Status: OrderStatus.Active }) { }257258// Acceptable259if (order != null) { }260261// Avoid262if (order is object) { }263if (!(order is null)) { }264265```text266267Use null-conditional and null-coalescing operators:268269```csharp270271var name = customer?.Name ?? "Unknown";272var orders = customer?.Orders ?? [];273items ??= [];274275```csharp276277### String Handling278279Prefer string interpolation over concatenation or `string.Format`:280281```csharp282283// Preferred284var message = $"Order {orderId} totals {total:C2}";285286// For complex interpolations, use raw string literals (C# 11+)287var json = $$"""288 {289 "id": {{orderId}},290 "name": "{{name}}"291 }292 """;293294// Avoid295var message = string.Format("Order {0} totals {1:C2}", orderId, total);296var message = "Order " + orderId + " totals " + total.ToString("C2");297298```text299300---301302## Access Modifiers303304Always specify access modifiers explicitly. Do not rely on defaults:305306```csharp307308// Correct309public class OrderService310{311 private readonly IOrderRepository _repo;312 internal void ProcessBatch() { }313}314315// Avoid: implicit internal class, implicit private field316class OrderService317{318 readonly IOrderRepository _repo;319}320321```text322323### Modifier Order324325Follow the standard order:326327```text328329access (public/private/protected/internal) -> static -> extern -> new ->330virtual/abstract/override/sealed -> readonly -> volatile -> async -> partial331332```text333334```csharp335336public static readonly int MaxSize = 100;337protected virtual async Task<Order> LoadAsync() => await repo.GetDefaultAsync();338public sealed override string ToString() => Name;339340```csharp341342---343344## Type Design345346These conventions implement SOLID and DRY principles at the code level. For comprehensive coverage with anti-patterns347and fixes, see [skill:dotnet-solid-principles].348349### Seal Classes by Default350351Seal classes that are not designed for inheritance. This improves performance (devirtualization) and communicates352intent:353354```csharp355356public sealed class OrderService(IOrderRepository repo)357{358 // Not designed for inheritance359}360361```text362363Only leave classes unsealed when you explicitly design them as base classes.364365### Prefer Composition Over Inheritance366367```csharp368369// Preferred: composition370public sealed class OrderProcessor(IValidator validator, INotifier notifier)371{372 public async Task ProcessAsync(Order order)373 {374 await validator.ValidateAsync(order);375 await notifier.NotifyAsync(order);376 }377}378379// Avoid: deep inheritance hierarchies380public class BaseProcessor { }381public class ValidatingProcessor : BaseProcessor { }382public class NotifyingValidatingProcessor : ValidatingProcessor { }383384```text385386### Interface Segregation387388Keep interfaces focused. Prefer multiple small interfaces over one large one:389390```csharp391392// Preferred393public interface IOrderReader394{395 Task<Order?> GetByIdAsync(int id, CancellationToken ct = default);396 Task<IReadOnlyList<Order>> GetAllAsync(CancellationToken ct = default);397}398399public interface IOrderWriter400{401402## Detailed Examples403404See [references/detailed-examples.md](references/detailed-examples.md) for complete code samples and advanced patterns.