如何更改 C# Record 构造函数的行为
如何更改 C# Record 構(gòu)造函數(shù)的行為
Record[1]?是 C# 9 中的一個(gè)新功能。Record是從Structs[2]借用的特殊類, 因?yàn)樗鼈兙哂?基于值的相等性,您可以將它們視為兩類類型之間的混合體。默認(rèn)情況下,它們或多或少是不可變的,并且具有語(yǔ)法糖,使聲明更容易和更簡(jiǎn)潔。但是,語(yǔ)法糖可能會(huì)掩蓋更多標(biāo)準(zhǔn)任務(wù),例如更改默認(rèn)構(gòu)造函數(shù)的行為。在某些情況下,您可能需要這樣做以進(jìn)行驗(yàn)證。本文將向您展示如何實(shí)現(xiàn)這一目標(biāo)。
以這個(gè)簡(jiǎn)單的示例類為例:
public class StringValidator {public string InputString { get; }public StringValidator(string inputString){if (string.IsNullOrEmpty(inputString)) throw new ArgumentNullException(nameof(inputString));InputString = inputString;} }很明顯,如果消費(fèi)者試圖在沒(méi)有有效字符串的情況下創(chuàng)建此類的實(shí)例,他們將收到異常。創(chuàng)建Record的標(biāo)準(zhǔn)語(yǔ)法如下所示:
public record StringValidator(string InputString);它既友好又簡(jiǎn)潔,但并不清楚您將如何驗(yàn)證字符串。此定義告訴編譯器將有一個(gè)名為 的屬性InputString,并且構(gòu)造函數(shù)會(huì)將值從參數(shù)傳遞給該屬性。我們需要?jiǎng)h除語(yǔ)法糖來(lái)驗(yàn)證字符串。幸運(yùn)的是,這很容易。我們不需要使用新語(yǔ)法來(lái)定義我們的Record。我們可以定義類似于類的Record,但將關(guān)鍵字類更改為Record。
public record StringValidator {public string InputString { get; }public StringValidator(string inputString){if (string.IsNullOrEmpty(inputString)) throw new ArgumentNullException(nameof(inputString));InputString = inputString;} }不幸的是,這意味著我們不能使用?非破壞性突變[3]。該with關(guān)鍵字給我們創(chuàng)造了一些屬性來(lái)更改Record的新版本的功能。這意味著我們不會(huì)修改Record的原始實(shí)例,但我們會(huì)得到它的副本。這是 Fluent API 和函數(shù)式編程的常用方法。這使我們能夠保持不變性。
為了允許非破壞性突變,我們需要添加init屬性訪問(wèn)器。這與構(gòu)造函數(shù)的工作方式類似,但僅在對(duì)象初始化期間調(diào)用。這是實(shí)現(xiàn)init訪問(wèn)器的更完整的解決方案。這允許您共享構(gòu)造函數(shù)邏輯和初始化邏輯。
using System;namespace ConsoleApp25 {class Program{static void Main(string[] args){//This throws an exception from the constructor//var stringValidator = new StringValidator(null);var stringValidator1 = new StringValidator("First");var stringValidator2 = stringValidator1 with { InputString = "Second" };Console.WriteLine(stringValidator2.InputString);//This throws an exception from the init accessor//var stringValidator3 = stringValidator1 with { InputString = null };//Output: Second}}public record StringValidator{private string inputString;public string InputString{get => inputString;init{//This init accessor works like the set accessorValidateInputString(value);inputString = value;}}public StringValidator(string inputString){ValidateInputString(inputString);InputString = inputString;}public static void ValidateInputString(string inputString){if (string.IsNullOrEmpty(inputString)) throw new ArgumentNullException(nameof(inputString));}} }Record構(gòu)造函數(shù)應(yīng)該有邏輯嗎?
這是一個(gè)有爭(zhēng)議的辯論,超出了本文的范圍。很多人會(huì)爭(zhēng)辯說(shuō)你不應(yīng)該把邏輯放在構(gòu)造函數(shù)中。Record的設(shè)計(jì)鼓勵(lì)您不要在構(gòu)造函數(shù)或 init 訪問(wèn)器中放置邏輯。一般來(lái)說(shuō),Record應(yīng)該及時(shí)代表數(shù)據(jù)的快照狀態(tài)。您不需要應(yīng)用邏輯,因?yàn)榧僭O(shè)您知道此時(shí)數(shù)據(jù)的狀態(tài)。但是,就像其他所有編程結(jié)構(gòu)一樣,無(wú)法知道Record可能會(huì)產(chǎn)生哪些用例。這是庫(kù)?Urls 中的[4]一個(gè)示例[5],?[6]它將 URL 視為不可變Record:
using System.Net;namespace Urls {public record QueryParameter{private string? fieldValue;public string FieldName { get; init; }public string? Value{get => fieldValue; init{fieldValue = WebUtility.UrlDecode(value);}}public QueryParameter(string fieldName, string? value){FieldName = fieldName;fieldValue = WebUtility.UrlDecode(value);}public override string ToString()=> $"{FieldName}{(Value != null ? "=" : "")}{WebUtility.UrlEncode(Value)}";} }我們確保在存儲(chǔ)查詢值時(shí)對(duì)其進(jìn)行解碼,然后在將其用作 Url 的一部分時(shí)對(duì)其進(jìn)行編碼。
你可能會(huì)問(wèn):為什么不把一切都Record下來(lái)?似乎會(huì)有與此相關(guān)的陷阱,但我們正在冒險(xiǎn)進(jìn)入新領(lǐng)域,我們尚未為 C# 上下文中的Record制定最佳實(shí)踐。
總結(jié)
開(kāi)發(fā)人員需要幾年時(shí)間才能接受Record并制定使用它們的基本規(guī)則。您目前有一張白紙,您可以自由嘗試,直到“專家”開(kāi)始告訴您其他情況。我的建議是只使用Record來(lái)表示固定數(shù)據(jù)和最小邏輯。盡可能使用語(yǔ)法糖。但是,在某些情況下,構(gòu)造函數(shù)中的最小驗(yàn)證可能是可行的。運(yùn)用你的判斷力,與你的團(tuán)隊(duì)討論,權(quán)衡利弊。
References
[1]?Record:?https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records
[2]?Structs:?https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct
[3]?非破壞性突變:?https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#non-destructive-mutation
[4]?Urls 中的:?https://github.com/MelbourneDeveloper/Urls
[5]?示例:?https://github.com/MelbourneDeveloper/Urls/blob/5f55a9437cfac1223711d616bfdbeb72b230d263/src/Uris/QueryParameter.cs#L5
[6]?, :?https://github.com/MelbourneDeveloper/Urls
總結(jié)
以上是生活随笔為你收集整理的如何更改 C# Record 构造函数的行为的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: WPF ComboBox 使用 Reso
- 下一篇: ASP.NET Core使用Middle