首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >7个“危险”高效的C#特性:让冗余代码彻底消失

7个“危险”高效的C#特性:让冗余代码彻底消失

作者头像
郑子铭
发布2025-09-02 17:36:14
发布2025-09-02 17:36:14
14100
代码可运行
举报
运行总次数:0
代码可运行

你的工作不是写setter、空值检查或try-catch-finally,但大多数C#代码库却逼着你做这些。

现代C#(9-13版本)通过以下特性消除了这些样板代码:

  • • 编译器强制的契约
  • • 运行时优化的行为
  • • 微软背书的模式(在Azure和Copilot中使用)

本文将介绍7个“危险”高效的特性,让你删掉那些本就不该存在的代码。

🎞️ 深入探讨前:先看看痛点(和解决方案)

🔐 1. required + init:告别构造函数

这对组合让你在编译时快速发现错误,无需构造函数、魔术字符串和空值,就能构建DTO和配置。

✅ 之前的写法:

代码语言:javascript
代码运行次数:0
运行
复制
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+):

代码语言:javascript
代码运行次数:0
运行
复制
public class User {
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    // 编译时检查必填项,属性不可变
}

🧠 无需手动验证或构造函数。缺少必填属性会在编译时直接报错——这是最佳的错误发现时机。

📌 参考:required修饰符 — Microsoft Docs

🧠 2. 模式匹配:比if更智能

模式匹配已全面升级:

  • • 类型模式
  • • 属性模式
  • • 递归和列表模式(C# 12+)

✅ 实际API示例:

代码语言:javascript
代码运行次数:0
运行
复制
return request.Method switch {
    "GET" => HandleGet(request),
    "POST" => HandlePost(request),
    _ => Results.BadRequest()
};

✅ 属性模式:

代码语言:javascript
代码运行次数:0
运行
复制
if (person is Employee { Salary: > _000 }) {
    GiveBonus(person);
}

✅ 列表模式:

代码语言:javascript
代码运行次数:0
运行
复制
if (nums is [_, _, , ..])
    Console.WriteLine("在第三个位置找到42。");

🧠 借助深层模式,你甚至可以匹配嵌套的对象结构和集合——无需空值链和类型检查。

📌 参考:模式匹配 — Microsoft Docs

🧬 3. record + with:不可变的清晰表达

想要100%类型安全、不可变的数据模型,又不想用AutoMapper或反射?

试试record和with。

✅ 之前的写法:

代码语言:javascript
代码运行次数:0
运行
复制
var updated = new Order {
    Id = original.Id,
    Customer = original.Customer,
    Status = "Shipped"
};

✅ 之后的写法:

代码语言:javascript
代码运行次数:0
运行
复制
var updated = original with { Status = "Shipped" };

✔ 无需映射工具 ✔ 无易变性bug ✔ 语言内置的复制语义

📌 参考:record类型 — Microsoft Docs

🔥 4. InterpolatedStringHandler:零分配日志

.NET 6为日志添加了编译器魔法: 如果日志级别被禁用,插值字符串甚至不会被计算——无分配,无性能损耗。

✅ 不推荐:

代码语言:javascript
代码运行次数:0
运行
复制
_logger.LogDebug($"Order {order.Id} processed for {order.Customer}");

✅ 推荐:

代码语言:javascript
代码运行次数:0
运行
复制
_logger.LogDebug("Order {OrderId} processed for {Customer}", order.Id, order.Customer);

✅ 快速 ✅ 结构化 ✅ 零插值成本

🧠 Microsoft.Extensions.Logging在幕后使用InterpolatedStringHandler优化日志格式化——而LoggerMessage.Define()通过预编译委托让这一过程更快。

📌 参考:InterpolatedStringHandler — Microsoft Docs

🧩 5. CallerArgumentExpression:告别nameof()

抛出异常时,想让参数名自动填充?

✅ 定义一次:

代码语言:javascript
代码运行次数:0
运行
复制
public static void ThrowIfNull<T>(
    T argument,
    [CallerArgumentExpression("argument")] string? name = null)
    => _ = argument ?? throw new ArgumentNullException(name);

✅ 随处使用:

代码语言:javascript
代码运行次数:0
运行
复制
ThrowIfNull(user); // 异常信息:“值不能为 null。(参数 'user')”

🧠 一个可重用的辅助方法,替代数十个繁琐的nameof()调用。

📌 参考:CallerArgumentExpression — Microsoft Docs

⚙️ 6. await using:无痛异步清理

如果你的类型实现了IAsyncDisposable,这应该成为你的新默认写法。

✅ 之前的写法:

代码语言:javascript
代码运行次数:0
运行
复制
var conn = await factory.CreateAsync();
try {
    await conn.SendAsync(...);
}
finally {
    await conn.DisposeAsync();
}

✅ 之后的写法:

代码语言:javascript
代码运行次数:0
运行
复制
await using var conn = await factory.CreateAsync();
await conn.SendAsync(...);

🧠 尤其在Blazor、EF Core和ASP.NET中非常有用,这些场景中异步流或DbContext很常见。

在Entity Framework Core的DbContext中使用时,可防止异步泄漏并提高负载下的性能。

📌 参考:IAsyncDisposable — Microsoft Docs

⚡ 7. 源生成器:替代反射,提升性能

既然可以在构建时生成代码,何必在运行时反射?

微软在以下组件中使用了源生成器:

  • • System.Text.Json
  • • Microsoft.Extensions.Logging
  • • EF Core元数据

✅ 示例:JSON源生成

代码语言:javascript
代码运行次数:0
运行
复制
[JsonSerializable(typeof(Order))]
internal partial class OrderJsonContext : JsonSerializerContext { }

🧠 这避免了ASP.NET中的反射,并在AOT场景中减小了输出大小。

需要在csproj中设置JsonSourceGenerationMode或通过JsonSerializerContext设置。

代码语言:javascript
代码运行次数:0
运行
复制
<ItemGroup>
  <PackageReference Include="System.Text.Json" Version="8.0.0" />
</ItemGroup>

📌 参考:System.Text.Json源生成 — Microsoft Docs

🎁 bonus:file修饰符 = 真正的文件作用域类型

想要一个仅对当前文件可见的辅助类?

✅ 这样写:

代码语言:javascript
代码运行次数:0
运行
复制
file class Helper {
    // 无法从项目的其他地方访问
}

🧠 非常适合内部静态辅助工具、小型DSL或测试脚手架。

📌 参考:file作用域类型 — C# 12 Docs

✅ 摘要表

(原内容未提供具体表格内容,此处保持原样)

🚀 最终挑战:从你的应用中删除100行代码

✅ 下一个PR建议:

  • • 用record + required + with重构一个DTO
  • • 用CallerArgumentExpression替代一个nameof()辅助方法
  • • 把一个try-finally换成await using

💡 然后运行差异对比。看着样板代码消失——且不会破坏任何测试。

🙋 轮到你了

这些特性中,你已经在使用哪些? 你会在下一个项目中重构哪一个?

👉 留下评论或分享给你的团队。 因为简洁、富有表现力的现代C#并不“危险”——而是高效到“致命”。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🎞️ 深入探讨前:先看看痛点(和解决方案)
    • 🔐 1. required + init:告别构造函数
    • 🧠 2. 模式匹配:比if更智能
    • 🧬 3. record + with:不可变的清晰表达
    • 🔥 4. InterpolatedStringHandler:零分配日志
    • 🧩 5. CallerArgumentExpression:告别nameof()
    • ⚙️ 6. await using:无痛异步清理
    • ⚡ 7. 源生成器:替代反射,提升性能
    • 🎁 bonus:file修饰符 = 真正的文件作用域类型
    • ✅ 摘要表
    • 🚀 最终挑战:从你的应用中删除100行代码
    • 🙋 轮到你了
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档