虽然我来晚了,但这可能仍然对某些人有所帮助...
实际上有一个简单的解决方案(在使用之前请先阅读下面的警告)。像这样定义一个基本的记录类型:
public abstract record RecordWithValidation
{
protected RecordWithValidation()
{
Validate();
}
protected virtual void Validate()
{
}
}
让您的实际记录继承RecordWithValidation
并覆盖Validate
:
record Person(Guid Id, string FirstName, string LastName, int Age) : RecordWithValidation
{
protected override void Validate()
{
if (FirstName == null)
throw new ArgumentException("Argument cannot be null.", nameof(FirstName));
if (LastName == null)
throw new ArgumentException("Argument cannot be null.", nameof(LastName));
if (Age < 0)
throw new ArgumentException("Argument cannot be negative.", nameof(Age));
}
}
正如您所看到的,这几乎是原作者的代码。它很简单,而且有效。
但是,如果您使用此代码,请非常小心:它仅适用于使用“位置记录”语法(又称“主构造函数”)定义的属性。
原因是我在这里做了一些“不好”的事情:我从基类型的构造函数中调用了虚拟方法。通常不建议这样做,因为基类型的构造函数先于派生类型的构造函数运行,所以派生类型可能尚未完全初始化,因此重写的方法可能无法正确工作。
但是对于位置记录,事情并非按照那种顺序发生:位置属性首先被初始化,然后调用基类型的构造函数。因此,当调用Validate
方法时,属性已经初始化,因此它可以按预期工作。
如果将Person
记录更改为具有显式构造函数(或仅可初始化属性并且没有构造函数),则会在设置属性之前调用Validate
,因此它将失败。
编辑:此方法的另一个令人讨厌的限制是它不适用于with
(例如 person with { Age = 42 }
)。这使用不同的(生成的)构造函数,该构造函数不调用Validate
...
!
。 - canton7with { .. }
只适用于init
属性,因此如果您定义自己的构造函数,您将失去使用 withers 的能力。 - canton7