这篇文章主要介绍如何更改C#中Record构造函数的行为,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
Record是 C# 9 中的一个新功能。Record是从Structs借用的特殊类, 因为它们具有 基于值的相等性,您可以将它们视为两类类型之间的混合体。默认情况下,它们或多或少是不可变的,并且具有语法糖,使声明更容易和更简洁。但是,语法糖可能会掩盖更多标准任务,例如更改默认构造函数的行为。在某些情况下,您可能需要这样做以进行验证。本文将向您展示如何实现这一目标。
以这个简单的示例类为例:
public class StringValidator { public string InputString { get; } public StringValidator(string inputString) { if (string.IsNullOrEmpty(inputString)) throw new ArgumentNullException(nameof(inputString)); InputString = inputString; } }
很明显,如果消费者试图在没有有效字符串的情况下创建此类的实例,他们将收到异常。创建Record的标准语法如下所示:
public record StringValidator(string InputString);
它既友好又简洁,但并不清楚您将如何验证字符串。此定义告诉编译器将有一个名为 的属性InputString,并且构造函数会将值从参数传递给该属性。我们需要删除语法糖来验证字符串。幸运的是,这很容易。我们不需要使用新语法来定义我们的Record。我们可以定义类似于类的Record,但将关键字类更改为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关键字给我们创造了一些属性来更改Record的新版本的功能。这意味着我们不会修改Record的原始实例,但我们会得到它的副本。这是 Fluent API 和函数式编程的常用方法。这使我们能够保持不变性。
为了允许非破坏性突变,我们需要添加init属性访问器。这与构造函数的工作方式类似,但仅在对象初始化期间调用。这是实现init访问器的更完整的解决方案。这允许您共享构造函数逻辑和初始化逻辑。
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 accessor ValidateInputString(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的设计鼓励您不要在构造函数或 init 访问器中放置逻辑。一般来说,Record应该及时代表数据的快照状态。您不需要应用逻辑,因为假设您知道此时数据的状态。但是,就像其他所有编程结构一样,无法知道Record可能会产生哪些用例。这是库 Urls 中的[4]一个示例[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)}"; } }
我们确保在存储查询值时对其进行解码,然后在将其用作 Url 的一部分时对其进行编码。
以上是“如何更改C#中Record构造函数的行为”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。