.NET 开发模式
用于构建健壮、高性能且可维护应用程序的惯用 C# 和 .NET 模式。
何时激活
- 编写新的 C# 代码时
- 审查 C# 代码时
- 重构现有 .NET 应用程序时
- 使用 ASP.NET Core 设计服务架构时
核心原则
1. 优先使用不可变性
对数据模型使用记录和仅初始化属性。可变性应作为明确且有理由的选择。
csharp
// Good: Immutable value objectpublic sealed record Money(decimal Amount, string Currency);
// Good: Immutable DTO with init setterspublic sealed class CreateOrderRequest{ public required string CustomerId { get; init; } public required IReadOnlyList<OrderItem> Items { get; init; }}
// Bad: Mutable model with public setterspublic class Order{ public string CustomerId { get; set; } public List<OrderItem> Items { get; set; }}2. 显式优于隐式
明确表达可空性、访问修饰符和意图。
csharp
// Good: Explicit access modifiers and nullabilitypublic sealed class UserService{ private readonly IUserRepository _repository; private readonly ILogger<UserService> _logger;
public UserService(IUserRepository repository, ILogger<UserService> logger) { _repository = repository ?? throw new ArgumentNullException(nameof(repository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); }
public async Task<User?> FindByIdAsync(Guid id, CancellationToken cancellationToken) { return await _repository.FindByIdAsync(id, cancellationToken); }}3. 依赖抽象
对服务边界使用接口。通过依赖注入容器注册。
csharp
// Good: Interface-based dependencypublic interface IOrderRepository{ Task<Order?> FindByIdAsync(Guid id, CancellationToken cancellationToken); Task<IReadOnlyList<Order>> FindByCustomerAsync(string customerId, CancellationToken cancellationToken); Task AddAsync(Order order, CancellationToken cancellationToken);}
// Registrationbuilder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();异步/等待模式
正确使用异步
csharp
// Good: Async all the way, with CancellationTokenpublic async Task<OrderSummary> GetOrderSummaryAsync( Guid orderId, CancellationToken cancellationToken){ var order = await _repository.FindByIdAsync(orderId, cancellationToken) ?? throw new NotFoundException($"Order {orderId} not found");
var customer = await _customerService.GetAsync(order.CustomerId, cancellationToken);
return new OrderSummary(order, customer);}
// Bad: Blocking on asyncpublic OrderSummary GetOrderSummary(Guid orderId){ var order = _repository.FindByIdAsync(orderId, CancellationToken.None).Result; // Deadlock risk return new OrderSummary(order);}并行异步操作
csharp
// Good: Concurrent independent operationspublic async Task<DashboardData> LoadDashboardAsync(CancellationToken cancellationToken){ var ordersTask = _orderService.GetRecentAsync(cancellationToken); var metricsTask = _metricsService.GetCurrentAsync(cancellationToken); var alertsTask = _alertService.GetActiveAsync(cancellationToken);
await Task.WhenAll(ordersTask, metricsTask, alertsTask);
return new DashboardData( Orders: await ordersTask, Metrics: await metricsTask, Alerts: await alertsTask);}选项模式
将配置节绑定到强类型对象。
csharp
public sealed class SmtpOptions{ public const string SectionName = "Smtp";
public required string Host { get; init; } public required int Port { get; init; } public required string Username { get; init; } public bool UseSsl { get; init; } = true;}
// Registrationbuilder.Services.Configure<SmtpOptions>( builder.Configuration.GetSection(SmtpOptions.SectionName));
// Usage via injectionpublic class EmailService(IOptions<SmtpOptions> options){ private readonly SmtpOptions _smtp = options.Value;}结果模式
对预期失败返回显式成功/失败,而非抛出异常。
csharp
public sealed record Result<T>{ public bool IsSuccess { get; } public T? Value { get; } public string? Error { get; }
private Result(T value) { IsSuccess = true; Value = value; } private Result(string error) { IsSuccess = false; Error = error; }
public static Result<T> Success(T value) => new(value); public static Result<T> Failure(string error) => new(error);}
// Usagepublic async Task<Result<Order>> PlaceOrderAsync(CreateOrderRequest request){ if (request.Items.Count == 0) return Result<Order>.Failure("Order must contain at least one item");
var order = Order.Create(request); await _repository.AddAsync(order, CancellationToken.None); return Result<Order>.Success(order);}使用 EF Core 的仓储模式
csharp
public sealed class SqlOrderRepository : IOrderRepository{ private readonly AppDbContext _db;
public SqlOrderRepository(AppDbContext db) => _db = db;
public async Task<Order?> FindByIdAsync(Guid id, CancellationToken cancellationToken) { return await _db.Orders .Include(o => o.Items) .AsNoTracking() .FirstOrDefaultAsync(o => o.Id == id, cancellationToken); }
public async Task<IReadOnlyList<Order>> FindByCustomerAsync( string customerId, CancellationToken cancellationToken) { return await _db.Orders .Where(o => o.CustomerId == customerId) .OrderByDescending(o => o.CreatedAt) .AsNoTracking() .ToListAsync(cancellationToken); }
public async Task AddAsync(Order order, CancellationToken cancellationToken) { _db.Orders.Add(order); await _db.SaveChangesAsync(cancellationToken); }}中间件与管道
csharp
// Custom middlewarepublic sealed class RequestTimingMiddleware{ private readonly RequestDelegate _next; private readonly ILogger<RequestTimingMiddleware> _logger;
public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger) { _next = next; _logger = logger; }
public async Task InvokeAsync(HttpContext context) { var stopwatch = Stopwatch.StartNew(); try { await _next(context); } finally { stopwatch.Stop(); _logger.LogInformation( "Request {Method} {Path} completed in {ElapsedMs}ms with status {StatusCode}", context.Request.Method, context.Request.Path, stopwatch.ElapsedMilliseconds, context.Response.StatusCode); } }}最小 API 模式
csharp
// Organized with route groupsvar orders = app.MapGroup("/api/orders") .RequireAuthorization() .WithTags("Orders");
orders.MapGet("/{id:guid}", async ( Guid id, IOrderRepository repository, CancellationToken cancellationToken) =>{ var order = await repository.FindByIdAsync(id, cancellationToken); return order is not null ? TypedResults.Ok(order) : TypedResults.NotFound();});
orders.MapPost("/", async ( CreateOrderRequest request, IOrderService service, CancellationToken cancellationToken) =>{ var result = await service.PlaceOrderAsync(request, cancellationToken); return result.IsSuccess ? TypedResults.Created($"/api/orders/{result.Value!.Id}", result.Value) : TypedResults.BadRequest(result.Error);});守卫子句
csharp
// Good: Early returns with clear validationpublic async Task<ProcessResult> ProcessPaymentAsync( PaymentRequest request, CancellationToken cancellationToken){ ArgumentNullException.ThrowIfNull(request);
if (request.Amount <= 0) throw new ArgumentOutOfRangeException(nameof(request.Amount), "Amount must be positive");
if (string.IsNullOrWhiteSpace(request.Currency)) throw new ArgumentException("Currency is required", nameof(request.Currency));
// Happy path continues here without nesting var gateway = _gatewayFactory.Create(request.Currency); return await gateway.ChargeAsync(request, cancellationToken);}应避免的反模式
| 反模式 | 修复方案 |
|---|---|
async void 方法 | 返回 Task(事件处理程序除外) |
.Result 或 .Wait() | 使用 await |
catch (Exception) { } | 处理或带上下文重新抛出 |
构造函数中的 new Service() | 使用构造函数注入 |
public 字段 | 使用带适当访问器的属性 |
业务逻辑中的 dynamic | 使用泛型或显式类型 |
可变的 static 状态 | 使用依赖注入作用域或 ConcurrentDictionary |
循环中的 string.Format | 使用 StringBuilder 或内插字符串处理程序 |


