你的工作不是写setter、空值检查或try-catch-finally,但大多数C#代码库却逼着你做这些。
现代C#(9-13版本)通过以下特性消除了这些样板代码:
本文将介绍7个“危险”高效的特性,让你删掉那些本就不该存在的代码。
这对组合让你在编译时快速发现错误,无需构造函数、魔术字符串和空值,就能构建DTO和配置。
✅ 之前的写法:
public class User {
public string FirstName { get; set; }
public string LastName { get; set; }
public User(string first, string last) {
FirstName = first;
LastName = last;
}
}
✅ 之后的写法(C# 11+):
public class User {
public required string FirstName { get; init; }
public required string LastName { get; init; }
// 编译时检查必填项,属性不可变
}
🧠 无需手动验证或构造函数。缺少必填属性会在编译时直接报错——这是最佳的错误发现时机。
📌 参考:required修饰符 — Microsoft Docs
模式匹配已全面升级:
✅ 实际API示例:
return request.Method switch {
"GET" => HandleGet(request),
"POST" => HandlePost(request),
_ => Results.BadRequest()
};
✅ 属性模式:
if (person is Employee { Salary: > 100_000 }) {
GiveBonus(person);
}
✅ 列表模式:
if (nums is [_, _, 42, ..])
Console.WriteLine("在第三个位置找到42。");
🧠 借助深层模式,你甚至可以匹配嵌套的对象结构和集合——无需空值链和类型检查。
想要100%类型安全、不可变的数据模型,又不想用AutoMapper或反射?
试试record和with。
✅ 之前的写法:
var updated = new Order {
Id = original.Id,
Customer = original.Customer,
Status = "Shipped"
};
✅ 之后的写法:
var updated = original with { Status = "Shipped" };
✔ 无需映射工具 ✔ 无易变性bug ✔ 语言内置的复制语义
📌 参考:record类型 — Microsoft Docs
.NET 6为日志添加了编译器魔法: 如果日志级别被禁用,插值字符串甚至不会被计算——无分配,无性能损耗。
✅ 不推荐:
_logger.LogDebug($"Order {order.Id} processed for {order.Customer}");
✅ 推荐:
_logger.LogDebug("Order {OrderId} processed for {Customer}", order.Id, order.Customer);
✅ 快速 ✅ 结构化 ✅ 零插值成本
🧠 Microsoft.Extensions.Logging在幕后使用InterpolatedStringHandler优化日志格式化——而LoggerMessage.Define()通过预编译委托让这一过程更快。
📌 参考:InterpolatedStringHandler — Microsoft Docs
抛出异常时,想让参数名自动填充?
✅ 定义一次:
public static void ThrowIfNull<T>(
T argument,
[CallerArgumentExpression("argument")] string? name = null)
=> _ = argument ?? throw new ArgumentNullException(name);
✅ 随处使用:
ThrowIfNull(user); // 异常信息:“值不能为 null。(参数 'user')”
🧠 一个可重用的辅助方法,替代数十个繁琐的nameof()调用。
📌 参考:CallerArgumentExpression — Microsoft Docs
如果你的类型实现了IAsyncDisposable,这应该成为你的新默认写法。
✅ 之前的写法:
var conn = await factory.CreateAsync();
try {
await conn.SendAsync(...);
}
finally {
await conn.DisposeAsync();
}
✅ 之后的写法:
await using var conn = await factory.CreateAsync();
await conn.SendAsync(...);
🧠 尤其在Blazor、EF Core和ASP.NET中非常有用,这些场景中异步流或DbContext很常见。
在Entity Framework Core的DbContext中使用时,可防止异步泄漏并提高负载下的性能。
📌 参考:IAsyncDisposable — Microsoft Docs
既然可以在构建时生成代码,何必在运行时反射?
微软在以下组件中使用了源生成器:
✅ 示例:JSON源生成
[JsonSerializable(typeof(Order))]
internal partial class OrderJsonContext : JsonSerializerContext { }
🧠 这避免了ASP.NET中的反射,并在AOT场景中减小了输出大小。
需要在csproj中设置JsonSourceGenerationMode或通过JsonSerializerContext设置。
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.0" />
</ItemGroup>
📌 参考:System.Text.Json源生成 — Microsoft Docs
想要一个仅对当前文件可见的辅助类?
✅ 这样写:
file class Helper {
// 无法从项目的其他地方访问
}
🧠 非常适合内部静态辅助工具、小型DSL或测试脚手架。
(原内容未提供具体表格内容,此处保持原样)
✅ 下一个PR建议:
💡 然后运行差异对比。看着样板代码消失——且不会破坏任何测试。
这些特性中,你已经在使用哪些? 你会在下一个项目中重构哪一个?
👉 留下评论或分享给你的团队。 因为简洁、富有表现力的现代C#并不“危险”——而是高效到“致命”。