AgentSkillsCN

Csharp Standards

C#标准

SKILL.md

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

ElementConventionExample
Classes, methods, propertiesPascalCaseProjectsController, GetProject
Variables, parameterscamelCaseprojectsService, userId
Private fields, primary constructor parameters_camelCase_cachedData, _isInitialized, _projectsService
ConstantsUPPER_CASEMAX_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] not First(), Count > 0 not Any(), HashSet for lookups, Span/ReadOnlySpan for memory efficiency, StringBuilder for 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.