七個“危險”高效的C#特性:讓冗余代碼徹底消失
為什么這篇博客很重要
你的工作不是寫setter、空值檢查或try-catch-finally,但大多數(shù)C#代碼庫卻逼著你做這些。
現(xiàn)代C#(9-13版本)通過以下特性消除了這些樣板代碼:
? 編譯器強制的契約
? 運行時優(yōu)化的行為
? 微軟背書的模式(在Azure和Copilot中使用)
本文將介紹7個“危險”高效的特性,讓你刪掉那些本就不該存在的代碼。
深入探討前:先看看痛點(和解決方案)
1. required + init:告別構造函數(shù)
這對組合讓你在編譯時快速發(fā)現(xiàn)錯誤,無需構造函數(shù)、魔術字符串和空值,就能構建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; }
    // 編譯時檢查必填項,屬性不可變
}無需手動驗證或構造函數(shù)。缺少必填屬性會在編譯時直接報錯——這是最佳的錯誤發(fā)現(xiàn)時機。
參考:required修飾符 — Microsoft Docs
2. 模式匹配:比if更智能
模式匹配已全面升級:
? 類型模式
? 屬性模式
? 遞歸和列表模式(C# 12+)
實際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。");借助深層模式,你甚至可以匹配嵌套的對象結構和集合——無需空值鏈和類型檢查。
參考:模式匹配 — Microsoft Docs
3. record + with:不可變的清晰表達
想要100%類型安全、不可變的數(shù)據(jù)模型,又不想用AutoMapper或反射?
試試record和with。
之前的寫法:
var updated = new Order {
    Id = original.Id,
    Customer = original.Customer,
    Status = "Shipped"
};之后的寫法:
var updated = original with { Status = "Shipped" };? 無需映射工具
? 無易變性bug
? 語言內(nèi)置的復制語義
參考:record類型 — Microsoft Docs
4. InterpolatedStringHandler:零分配日志
.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優(yōu)化日志格式化——而LoggerMessage.Define()通過預編譯委托讓這一過程更快。
參考:InterpolatedStringHandler — Microsoft Docs
5. CallerArgumentExpression:告別nameof()
拋出異常時,想讓參數(shù)名自動填充?
定義一次:
public static void ThrowIfNull<T>(
    T argument,
    [CallerArgumentExpression("argument")] string? name = null)
    => _ = argument ?? throw new ArgumentNullException(name);隨處使用:
ThrowIfNull(user); // 異常信息:“值不能為 null。(參數(shù) 'user')”一個可重用的輔助方法,替代數(shù)十個繁瑣的nameof()調(diào)用。
參考:CallerArgumentExpression — Microsoft Docs
6. await using:無痛異步清理
如果你的類型實現(xiàn)了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
7. 源生成器:替代反射,提升性能
既然可以在構建時生成代碼,何必在運行時反射?
微軟在以下組件中使用了源生成器:
? System.Text.Json
? Microsoft.Extensions.Logging
? EF Core元數(shù)據(jù)
示例: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
bonus:file修飾符 = 真正的文件作用域類型
想要一個僅對當前文件可見的輔助類?
這樣寫:
file class Helper {
    // 無法從項目的其他地方訪問
}非常適合內(nèi)部靜態(tài)輔助工具、小型DSL或測試腳手架。
參考:file作用域類型 — C# 12 Docs
摘要表
(原內(nèi)容未提供具體表格內(nèi)容,此處保持原樣)
最終挑戰(zhàn):從你的應用中刪除100行代碼
下一個PR建議:
? 用record + required + with重構一個DTO
? 用CallerArgumentExpression替代一個nameof()輔助方法
? 把一個try-finally換成await using
?? 然后運行差異對比??粗鴺影宕a消失——且不會破壞任何測試。
輪到你了
這些特性中,你已經(jīng)在使用哪些?
你會在下一個項目中重構哪一個?















 
 
 








 
 
 
 