【发布时间】:2020-12-19 05:44:08
【问题描述】:
重新开放的免责声明
这个问题是关于使用 C# 9 init-only setters 对 不可变 对象 在构造期间 进行 对象验证(而不是使用 构造函数,带有详细的"boiler plate code")。
C# 9 引入了一个使用对象初始化器语法初始化不可变对象的选项,仅使用 init setter:
class Immutable
{
public string Name { get; init; }
public int Value { get; init; }
}
Immutable o = new Immutable { Name = "Value1", Value = 257 };
此外,它还引入了一种很好的语法来创建对象的变异副本:
var o1 = o with { Value = 65537 };
以前创建和初始化新的不可变对象的唯一选择是使用带参数的构造函数。新选项更加自然和优雅,但是缺少构造函数初始化的一个重要特性:验证。使用构造函数,我可以确保永远不会创建状态无效的对象。
可以将验证代码放入 init setter 中,但据我所知,无法提供对整个对象状态的一般验证。具体来说,在上面的示例中,我看不到任何方法可以保证 Name 属性将履行其具有非空值的合同。由于使用对象初始化语法需要无参数构造函数,因此可以创建一个未初始化的实例:
var o = new Immutable();
在这种情况下,属性将获得默认值。
问题[编辑]:在使用 init setter 初始化完成后,是否有任何方法可以验证不可变对象的状态?请记住,初始化语句中可能未指定属性分配,并且默认对象状态可能无效。
【问题讨论】:
-
Specifically, I don't see any way to assure in the example above that Name property will fulfill its contract to have a non-null value.你能告诉我们你用初始化设置器做这件事的尝试吗? -
“因为无参数构造函数是使用对象初始化语法所必需的”——不是在 C# 中......你确定你不是在谈论别的东西吗?
-
@MickyD,当通过构造函数参数初始化对象时,我可以单独验证所有这些,但也可以相互关联。当然,我可以从所有相关的 setter 调用这样的验证,但它不是那么好,而且我最感兴趣的场景是我在创建对象时没有指定任何属性的特定情况(没有调用 setter )。我的主要问题:当我通过构造函数参数初始化创建一个不可变对象时,我可以保证任何创建的对象都是有效的。我不能保证使用无参数构造函数和初始化设置器。
-
@mjwills,Name属性是不可为空的字符串(启用#nullable),所以默认初始化后的值是无效的。我可以保证不能通过 setter 分配 null,但我不能保证完全分配了一些有效值。我可以保证使用带参数的构造函数。
-
@AlexeiLevenkov,公共构造函数是创建对象所必需的(或者如果从后代类调用则受保护) - 它可以是隐含的,由编译器自动生成,它也可以具有默认值的参数,但它必须存在。它在 init setter 之前被调用。
标签: c# initialization immutability c#-9.0