关于 modern-csharp
面向使用 C# 的开发者,提供 C# 14 和 .NET 10 的现代语言特性参考与实践建议。内容涵盖可读性、值类型、不可变性及常见反模式。
主要功能
- 01介绍 C# 14 语言特性
- 02展示集合表达式与模式匹配
- 03说明 field 关键字和扩展成员
- 04强调值类型与不可变性
- 05列举反模式及替代写法
适用场景
- 01更新 C# 项目的编码习惯
- 02为 DTO 和值对象选择类型
- 03减少字符串切片的分配
- 04改进属性验证与延迟初始化
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ codewithmukesh/ dotnet-claude-kit --skill modern-csharp安装后在 Claude Code 中输入/modern-csharp确认已加载。
详细介绍
现代 C#(C# 14 / .NET 10)
核心原则
- 使用最新的稳定功能 — 目标版本为 C# 14。优先使用语言级构造,而不是通过库变通实现。
- 可读性优先于巧妙 — 在适当情况下,模式匹配和表达式体成员可以提升可读性;深层嵌套的模式则不行。
- 尽可能使用值类型 — 优先使用
record struct、Span<T>和栈分配,以减少 GC 压力。 - 默认保持不可变 — 使用
record、readonly、init和required,让非法状态无法表示。
模式
常用功能速查
| 功能 | 用途 | 示例 |
|---|---|---|
| 主构造函数 | DI 注入,消除字段赋值 | public class OrderService(IOrderRepo repo, TimeProvider clock) { } |
| 集合表达式 | 所有集合类型均可使用 [] + 展开 | List<string> names = ["Alice", "Bob"]; / int[] all = [..a, ..b, 99]; |
| Records | DTO、值对象、不可变数据 | public record CreateOrderRequest(string CustomerId, List<OrderItem> Items); |
readonly record struct | 小型栈分配值类型 | public readonly record struct Money(decimal Amount, string Currency); |
| 模式匹配 | Switch 表达式、列表/属性模式 | order switch { { Total: > 1000 } => "Premium", _ => "Standard" }; |
| 列表模式 | 解构数组/列表 | items switch { [] => "Empty", [var x] => $"One: {x}", [var f, .., var l] => $"{f}..{l}" }; |
Span<T> | 零分配切片 | ReadOnlySpan<char> trimmed = input.Trim(); int.TryParse(trimmed[4..], out id); |
| 原始字符串字面量 | 多行 SQL、JSON、XML | var sql = """ SELECT ... """; / 插值形式:$$""" {"id": "{{id}}"} """; |
required 成员 | 强制初始化 | public required string ConnectionString { get; init; } |
is 模式 + 提取 | 空值/类型/属性检查 | if (result is { IsSuccess: true, Value: var order }) { ... } |
field 关键字(C# 14)
在属性访问器中访问自动生成的后备字段,无需手动声明。
// GOOD — field keyword for validation in auto-property
public class Product
{
public string Name
{
get => field;
set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}
public decimal Price
{
get => field;
set => field = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value));
}
}
使用 field 延迟初始化
public class ProductCatalog
{
// Lazy-load on first access — no manual Lazy<T> or backing field
public IReadOnlyList<Product> Products
{
get => field ??= LoadProducts();
}
private static List<Product> LoadProducts() => /* expensive load */;
}
使用 field 通知更改
// INotifyPropertyChanged without manual backing fields
public class OrderViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public string CustomerName
{
get => field;
set
{
if (field == value) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CustomerName)));
}
} = "";
public decimal Total
{
get => field;
set
{
if (field == value) return;
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Total)));
}
}
}
扩展成员(C# 14)
C# 14 在静态类中新增了 extension 块。与传统扩展方法不同,它支持扩展属性和静态扩展成员——整个块只需声明一次接收者。
// GOOD — extension block (shipped C# 14 syntax)
public static class OrderExtensions
{
extension(Order order)
{
public decimal TotalWithTax => order.Total * 1.2m;
public bool IsHighValue => order.Total > 1000m;
public string ToSummary() =>
$"Order #{order.Id}: {order.Total:C} ({order.Items.Count} items)";
}
// Static extension members use the type (no receiver instance)
extension(Order)
{
public static Order Empty => Order.Create("none", [], DateTimeOffset.MinValue);
}
}
// Callers see them as if declared on Order
if (order.IsHighValue) { /* ... */ }
传统的 this 参数扩展方法仍然有效,也可以共存——需要属性或针对同一接收者定义多个成员时,使用扩展块。
反模式
有现代替代方案时,不要使用过时模式
// BAD — manual backing field when field keyword works
private string _name;
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException();
}
// BAD — old-style collection initialization
var list = new List<int>() { 1, 2, 3 };
// BAD — Tuple instead of record for domain types
(string Name, decimal Price) product = ("Widget", 9.99m);
// GOOD — record
public record Product(string Name, decimal Price);
不要过度使用模式匹配
// BAD — deeply nested pattern that's hard to read
if (order is { Customer: { Address: { Country: { Code: "US" } } } })
// GOOD — extract to a clear method or use sequential checks
if (order.Customer.Address.Country.Code == "US")
类型不明显时,不要使用 var
// BAD — what type is this?
var result = Process(order);
// GOOD — explicit type when not obvious
Result<Order> result = Process(order);
// Also GOOD — var is fine when type is apparent
var orders = new List<Order>();
决策指南
| 场景 | 建议 |
|---|---|
| DTO / API 契约 | record(引用类型) |
| 小型值对象(2-3 个字段) | readonly record struct |
| 使用 DI 的服务 | 主构造函数 |
| 创建集合 | 集合表达式 [] |
| 带验证的属性 | field 关键字 |
| 多行字符串(SQL、JSON) | 原始字符串字面量 """ |
| 切片字符串/数组 | Span<T> |
| 类型检查 + 提取 | 使用 is / switch 进行模式匹配 |
| 强制初始化 | required 修饰符 |
| 为外部类型添加方法 | 扩展成员 |
评论
支持 Markdown