csharp-standards
C# Coding Standards & Conventions
Apply these standards when writing C# code for .NET projects.
Naming Conventions
1. PascalCase
Use for classes, interfaces, enums, methods, properties, and events:
public class UserService { }
public interface IUserRepository { }
public enum OrderStatus { }
public void ProcessOrder() { }
public string FirstName { get; set; }
2. camelCase
Use for local variables, parameters, and private fields:
public void CalculateTotal(int itemCount)
{
var totalPrice = itemCount * 10;
var discount = 0.1;
}
private int _orderCount; // Private field with underscore prefix
3. Interface Naming
Prefix interfaces with I:
public interface IRepository { }
public interface IUserService { }
public interface ILogger { }
Access Modifiers
4. Explicit Access Modifiers
Always specify access modifiers:
Good:
public class UserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository)
{
_repository = repository;
}
public async Task<User> GetUserAsync(int id)
{
return await _repository.FindByIdAsync(id);
}
private bool ValidateUser(User user)
{
return !string.IsNullOrEmpty(user.Email);
}
}
Bad:
class UserService // Missing public
{
IUserRepository _repository; // Missing private
}
Modern C# Features
5. Expression-Bodied Members
Use for simple properties and methods:
// Properties
public string FullName => $"{FirstName} {LastName}";
public bool IsActive => Status == UserStatus.Active;
// Methods
public int Add(int a, int b) => a + b;
public void LogError(string message) => _logger.LogError(message);
// Constructors
public User(string name) => Name = name;
6. Var Keyword
Use var when the type is obvious:
Good:
var user = new User(); // Type is obvious
var count = GetCount(); // Return type is clear
var items = new List<string>(); // Type is clear
Questionable:
var result = repository.GetData(); // What type is result?
Better:
List<User> users = repository.GetUsers(); // Explicit when helpful
7. Pattern Matching
Use modern pattern matching features:
// Type patterns
if (obj is User user)
{
Console.WriteLine(user.Name);
}
// Switch expressions
var discount = orderTotal switch
{
< 100 => 0,
< 500 => 0.05,
< 1000 => 0.10,
_ => 0.15
};
// Property patterns
var shipping = customer switch
{
{ IsPremium: true } => 0,
{ OrderCount: > 10 } => 5,
_ => 10
};
8. Null-Coalescing and Null-Conditional
// Null-coalescing
var name = user?.Name ?? "Guest";
// Null-conditional
var length = user?.Address?.City?.Length;
// Null-coalescing assignment (C# 8.0+)
_cache ??= new Dictionary<string, object>();
Code Structure
9. Always Use Braces
Use braces even for single statements:
Good:
if (user != null)
{
ProcessUser(user);
}
foreach (var item in items)
{
Console.WriteLine(item);
}
Bad:
if (user != null)
ProcessUser(user);
10. Avoid Magic Numbers
Use named constants or enums:
Bad:
if (status == 1)
{
// What does 1 mean?
}
Good:
private const int ActiveStatus = 1;
if (status == ActiveStatus)
{
// Clear intent
}
Better:
public enum UserStatus
{
Inactive = 0,
Active = 1,
Suspended = 2
}
if (status == UserStatus.Active)
{
// Best: self-documenting
}
11. String Literals
Use double quotes for strings, single quotes for chars:
string message = "Hello, World!";
char delimiter = ',';
Documentation
12. XML Documentation Comments
Document public APIs, classes, and methods:
/// <summary>
/// Calculates the sum of two integers.
/// </summary>
/// <param name="a">The first integer.</param>
/// <param name="b">The second integer.</param>
/// <returns>The sum of a and b.</returns>
/// <exception cref="OverflowException">
/// Thrown when the result exceeds Int32.MaxValue.
/// </exception>
public static int Add(int a, int b)
{
return checked(a + b);
}
/// <summary>
/// Represents a user in the system.
/// </summary>
public class User
{
/// <summary>
/// Gets or sets the user's unique identifier.
/// </summary>
public int Id { get; set; }
/// <summary>
/// Gets or sets the user's email address.
/// </summary>
public string Email { get; set; }
}
Async/Await
13. Async Method Naming
Suffix async methods with Async:
public async Task<User> GetUserAsync(int id)
{
return await _repository.FindByIdAsync(id);
}
public async Task SaveUserAsync(User user)
{
await _repository.SaveAsync(user);
}
14. Async Best Practices
// Always await async calls
public async Task ProcessOrderAsync(Order order)
{
await ValidateOrderAsync(order);
await SaveOrderAsync(order);
await SendConfirmationAsync(order);
}
// Use ConfigureAwait(false) in libraries
public async Task<Data> FetchDataAsync()
{
var response = await _httpClient.GetAsync(url).ConfigureAwait(false);
return await response.Content.ReadAsAsync<Data>().ConfigureAwait(false);
}
Complete Example
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyApp.Services
{
/// <summary>
/// Service for managing user operations.
/// </summary>
public class UserService : IUserService
{
private readonly IUserRepository _repository;
private readonly ILogger<UserService> _logger;
private const int MaxRetryAttempts = 3;
/// <summary>
/// Initializes a new instance of the <see cref="UserService"/> class.
/// </summary>
/// <param name="repository">The user repository.</param>
/// <param name="logger">The logger instance.</param>
public UserService(IUserRepository repository, ILogger<UserService> logger)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// Gets a user by their unique identifier.
/// </summary>
/// <param name="id">The user's ID.</param>
/// <returns>The user if found; otherwise, null.</returns>
public async Task<User?> GetUserByIdAsync(int id)
{
if (id <= 0)
{
throw new ArgumentException("ID must be greater than zero.", nameof(id));
}
try
{
var user = await _repository.FindByIdAsync(id);
return user;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving user with ID {UserId}", id);
throw;
}
}
/// <summary>
/// Creates a new user.
/// </summary>
/// <param name="user">The user to create.</param>
/// <returns>The created user with assigned ID.</returns>
public async Task<User> CreateUserAsync(User user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
if (!IsValidUser(user))
{
throw new ValidationException("Invalid user data.");
}
var createdUser = await _repository.AddAsync(user);
_logger.LogInformation("User created with ID {UserId}", createdUser.Id);
return createdUser;
}
/// <summary>
/// Validates user data.
/// </summary>
private bool IsValidUser(User user)
{
return !string.IsNullOrWhiteSpace(user.Email)
&& !string.IsNullOrWhiteSpace(user.Name);
}
/// <summary>
/// Gets the user's display name.
/// </summary>
public string GetDisplayName(User user) =>
user?.Name ?? "Guest";
}
}
Best Practices Summary
- ✅ Use PascalCase for public members, camelCase for private
- ✅ Always use explicit access modifiers
- ✅ Prefer expression-bodied members for simple code
- ✅ Use
varwhen type is obvious - ✅ Always use braces for control flow
- ✅ Document public APIs with XML comments
- ✅ Suffix async methods with
Async - ✅ Use pattern matching and modern C# features
- ✅ Avoid magic numbers
- ✅ End every statement with a semicolon
When to Apply
Apply these standards when:
- Writing C# code
- Creating classes and interfaces
- Reviewing C# code
- Setting up new .NET projects
- User asks about C# conventions
More from devbyray/github-copilot-starter
html-standards
Guidelines for writing HTML that is accessible, semantic, maintainable, and secure. Use when working with .html, .vue, .twig, .php, .cshtml, .svelte files, or when the user asks about HTML structure, semantic markup, accessibility, or web security best practices.
10markdown-standards
Documentation and content creation standards for Markdown files. Use when creating or editing .md files, writing documentation, README files, or when the user asks about Markdown formatting, content structure, front matter, or documentation best practices.
6css-standards
Guidelines for writing CSS that is simple, readable, and maintainable. Use when working with .css, .scss, .sass files, CSS-in-JS, styled-components, or when the user asks about CSS organization, selectors, specificity, reusability, performance, or responsive design patterns.
4javascript-typescript-standards
Guidelines for writing JavaScript and TypeScript code that is simple, readable, and maintainable. Use when working with .js, .jsx, .ts, .tsx, .mjs, .cjs files, or when the user asks about JavaScript/TypeScript coding standards, ES2022 features, Node.js modules, async/await patterns, or testing with Vitest.
4nuxt-development
Standards for Nuxt.js 3 development with auto-imports, file-based routing, and SSR/SSG capabilities. Use when working with Nuxt projects, pages/, layouts/, composables/ directories, or when the user asks about Nuxt-specific patterns, server-side rendering, or Nuxt configuration.
3testing-tdd
Test-Driven Development workflow and testing standards. Use when writing tests, implementing TDD, working with Vitest (frontend), xUnit or MsTest (backend), or when the user asks about testing strategies, test coverage, or TDD methodology.
3