C# Development Standards
Use these standards when writing C# code in the ProcurementHub project.
Modern C# 12 Syntax
csharp
// File-scoped namespaces with primary constructors
namespace ProcurementHub.Api.Controllers;
[ApiController, Route("api/[controller]/[action]")]
public sealed class ProjectsController(IProjectsService _projectsService) : ControllerBase
{
[HttpGet]
public async Task<Project> Get(int id) => await _projectsService.GetProject(id);
}
// Collection expressions, modern null checks, expression-bodied methods
var projects = [project1, project2, project3];
if (domain is not null && domain.IsActive) { }
string GetStatus() => IsActive ? "Active" : "Inactive";
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Classes, methods, properties | PascalCase | ProjectsController, GetProject |
| Variables, parameters | camelCase | projectsService, userId |
| Private fields, primary constructor parameters | _camelCase | _cachedData, _isInitialized, _projectsService |
| Constants | UPPER_CASE | MAX_RETRY_ATTEMPTS |
Service Pattern
csharp
public sealed class VendorsService(IProcurementHubDbConnectionFactory _connectionFactory)
{
readonly List<Vendor> _cachedData = [];
bool _isInitialized;
public async Task<List<Vendor>> Get(GetVendorsRequest request)
{
EnsureInitialized();
return await GetVendorsFromDatabase(request);
}
void EnsureInitialized()
{
if (_isInitialized) return;
// Initialize cached data
_isInitialized = true;
}
}
Best Practices Checklist
- •✅ File-scoped namespaces - Single namespace per file
- •✅ Primary constructors - For services and controllers (not Blazor components)
- •✅ Sealed classes - Seal classes not designed for inheritance (services, controllers, models, components)
- •✅ Collection expressions -
var list = [item1, item2]; - •✅ Modern null checking - Use
is not null - •✅ Expression-bodied methods - Single-line methods under 160 characters
- •✅ Async everywhere - All I/O operations, no "Async" suffix
- •✅ Omit private keyword - It's the default access modifier
- •✅ Use var - When type is obvious from context
- •✅ xml doc Summary - If the line would be less than 160 characters, put the summary on a single line. The summary tags should have a space between them and the text.
- •✅ Use expression-body members - When possible. If under 160 characters, keep it on a single line
- •✅ Static methods - Any method that does not use instance state
- •✅ Performance - Use
collection[0]notFirst(),Count > 0notAny(),HashSetfor lookups,Span/ReadOnlySpanfor memory efficiency,StringBuilderfor string concatenation in loops - •❌ Private properties - Use private fields instead
Sealed Classes: Always seal classes unless explicitly designed for inheritance. Benefits: JIT optimizations (devirtualization, method inlining), design clarity, and security.