C# 12與.NET 8實戰指南:20個提升代碼質量的最佳實踐
1. 使用 required + init 實現更安全的對象創建
確保某些屬性在對象創建時必須被設置。通過避免部分初始化的對象來減少錯誤。
public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
}
var user = new User { Name = "Alex", Email = "alex@email.com" };
? 強制開發者在對象創建時設置重要屬性。不再出現"糟糕,忘記設置郵箱"的bug。
2. 啟用可空引用類型。立刻行動。
讓編譯器對可能的空引用問題發出警告,在運行前發現問題。
#nullable enable
string? name = GetUserInput();
Console.WriteLine(name.Length); // 編譯器警告!
? 在編譯時捕獲潛在的空值問題,而不是在生產環境中救火。
3. 使用文件作用域類型實現真正封裝
保持工具類在文件內私有,避免污染全局命名空間,幫助強制執行邊界。
file class StringHelper
{
public static bool IsNullOrWhitespace(string? value) => string.IsNullOrWhiteSpace(value);
}
? 保持輔助方法在文件內私有。由編譯器強制執行的清晰架構。
4. 使用集合表達式獲得更簡潔語法
用更簡潔的語法創建數組和列表。無需使用new[]或new List<>()。
int[] numbers = [1, 2, 3];
List<string> names = ["Alice", "Bob"];
? 更少干擾,更多專注。數組和列表的使用感覺更自然。
5. 使用record處理不可變數據
Record是DTO和只讀數據的理想選擇。你獲得內置的相等性、不可變性和解構功能。
public record Product(string Id, string Name);
? 不可變性 = 安全性。完美適用于DTO和值對象。
6. 除非在UI代碼中,否則避免使用async void
async void會吞掉異常。優先使用Task或ValueTask,以便調用者可以await并正確處理錯誤。
public async Task SaveAsync() { } // ?
public async void SaveAsync() { } // ?
? Task讓你能夠捕獲異常、測試方法并正確await。
7. 在性能關鍵路徑中使用ValueTask
在性能關鍵代碼中節省內存。與Task不同,如果結果已準備就緒,ValueTask避免在堆上分配。
public ValueTask<string> GetTokenAsync() => new("cached_token");
? 減少分配。非常適合API或頻繁調用的異步方法。
8. 不要在屬性getter中放置邏輯
保持getter快速且無副作用。不要進行昂貴的數據庫調用或可能令人意外的邏輯。
public bool IsValid => _status == "Active"; // ?
public bool IsValid => ExpensiveDbCall(); // ?
? 屬性應該廉價且無副作用。
9. 使用[CallerArgumentExpression]改進驗證
通過捕獲傳遞給方法的實際表達式來改進驗證和日志記錄,使錯誤消息更清晰。
void Ensure(bool condition, [CallerArgumentExpression("condition")] string? msg = null)
{
if (!condition) throw new ArgumentException($"Check failed: {msg}");
}
? 在工具和驗證庫中超級方便。使日志更清晰。
10. 使用nameof()替代魔法字符串
用編譯器檢查的名稱替換魔法字符串。重構安全且避免拼寫錯誤。
logger.LogError(nameof(User.Email)); // ?
? 安全重構。無拼寫錯誤。不留遺憾。
11. 在日志記錄中使用插值字符串處理程序
現代日志記錄在日志級別關閉時跳過字符串創建,使其更快且內存高效。
logger.LogInformation($"Processing user {userId} at {DateTime.UtcNow}");
? 現代日志記錄器優化掉未使用的日志 = 關閉時零分配。
12. 在私有對象上鎖定——而不是this
鎖應該是內部和私有的,以防止死鎖和外部干擾。
private readonly object _lock = new();
lock (_lock) { /* safe work */ }
? 防止外部代碼鎖定你的對象而導致死鎖。
13. 保持方法專注且小巧
一個方法 = 一個職責。較小的方法更易于閱讀、測試和重用。
? 如果你的方法需要滾動查看,說明它做了太多事情。分解它。更易于測試和調試。
14. 使用dotnet format和分析器
dotnet format
? 強制執行干凈、一致的代碼。在PR審查中節省時間。
15. 對簡單應用使用頂級語句
在小型控制臺工具中無需樣板Main()方法。非常適合快速腳本和學習演示。
Console.WriteLine("Hello, clean world!");
? 最少的樣板代碼 = 更快的原型開發。
16. 使用Span和ReadOnlySpan進行切片
這些類型讓你可以在不分配內存的情況下處理內存切片。非常適合解析器和性能關鍵代碼。
ReadOnlySpan<char> span = "1234567890".AsSpan(2, 4);
? 零分配。非常適合解析器、分詞器和字符串操作。
17. 除非真正需要,否則避免使用dynamic
Dynamic禁用編譯時檢查和智能感知。優先使用強類型以盡早捕獲錯誤。
dynamic data = GetLegacyData(); // ?
? 你會失去編譯時安全性和IntelliSense。優先使用強類型。
18. 對小邏輯使用表達式主體成員
對短方法或屬性使用簡潔語法。更清晰且更易讀。
public int Double(int x) => x * 2;
? 更短、更清晰、更易讀。
19. 使用默認接口實現(謹慎使用)
通過允許包含默認行為為接口增加靈活性,但不要在此隱藏復雜性。
public interface IWorker
{
void Work() => Console.WriteLine("Working...");
}
? 允許演進API而不會破壞契約——但不要濫用它。
20. 不需要可變性時優先使用readonly struct
防止意外更改并提高性能。在處理坐標等值類型時最佳。
public readonly struct Point(int X, int Y);
? 防止意外突變。也有助于提高性能。
總結
? required + init
? 可空引用類型 = 開啟
? 避免async void
? 文件作用域輔助類
? 性能場景使用ValueTask
? 使用nameof()
? 使用Span
? 日志記錄 = 插值字符串
? 避免dynamic
? 保持方法小巧
? 使用分析器 + dotnet format
? 永遠不要在this上鎖定
? 表達式主體方法
? 使用[CallerArgumentExpression]
? 小型工具使用頂級語句
? DTO使用record而非class

浙公網安備 33010602011771號