简而言之,对于像这样实现的
record Foo
:var foo = new Foo {Value = "foo"}
和 var bar = new Foo {Value = "foo"}
,foo == bar
表达式将导致True
,即使它们具有不同的引用 (ReferenceEquals(foo,bar) // False
)。现在,即使在文章中提到:
但当我尝试放置如果您不喜欢生成的 Equals 覆盖默认的逐字段比较行为,则可以编写自己的覆盖。
public override bool Equals
、public override int GetHashCode
或 public static bool operator ==
等时,我会得到 Member with the same signature is already declared
的错误,因此我认为这是一种受限制的行为,这在struct
对象中并非如此。
失败的示例:public sealed record SimpleVo
: IEquatable<SimpleVo>
{
public bool Equals(SimpleVo other) =>
throw new System.NotImplementedException();
public override bool Equals(object obj) =>
obj is SimpleVo other && Equals(other);
public override int GetHashCode() =>
throw new System.NotImplementedException();
public static bool operator ==(SimpleVo left, SimpleVo right) =>
left.Equals(right);
public static bool operator !=(SimpleVo left, SimpleVo right) =>
!left.Equals(right);
}
编译器结果:
SimpleVo.cs(11,30): error CS0111: Type 'SimpleVo' already defines a member called 'Equals' with the same parameter types
SimpleVo.cs(17,37): error CS0111: Type 'SimpleVo' already defines a member called 'op_Equality' with the same parameter types
SimpleVo.cs(20,37): error CS0111: Type 'SimpleVo' already defines a member called 'op_Inequality' with the same parameter types
在这里,我的主要问题是如果我们想要自定义相等性检查器的工作方式怎么办?我的意思是,我确实理解这打破了记录的整个目的,但另一方面,相等性检查器并不是使记录使用起来很酷的唯一功能。
有些情况下,某人希望覆盖记录的相等性,例如可以使用一个属性来排除某个属性进行相等性检查。例如,考虑此实现的ValueObject
。
然后,如果你按如下方法扩展这个ValueObject
抽象类:
public sealed class FullNameVo : ValueObject
{
public FullNameVo(string name, string surname)
{
Name = name;
Surname = surname;
}
[IgnoreMember]
public string Name { get; }
public string Surname { get; }
[IgnoreMember]
public string FullName => $"{Name} {Surname}";
}
那么您将得到以下结果:
var user1 = new FullNameVo("John", "Doe");
var user2 = new FullNameVo("John", "Doe");
var user3 = new FullNameVo("Jane", "Doe");
Console.WriteLine(user1 == user2); // True
Console.WriteLine(ReferenceEquals(user1, user2)); // False
Console.WriteLine(user1 == user3); // True
Console.WriteLine(user1.Equals(user3)); // True
为了以某种方式实现上述用例,到目前为止,我已经实现了一个抽象记录对象并像这样利用它:
public sealed record FullNameVo : ValueObject
{
[IgnoreMember]
public string Name;
public string Surname;
[IgnoreMember]
public string FullName => $"{Name} {Surname}";
}
结果如下所示:
var user1 = new FullNameVo
{
Name = "John",
Surname = "Doe"
};
var user2 = new FullNameVo
{
Name = "John",
Surname = "Doe"
};
var user3 = user1 with { Name = "Jane" };
Console.WriteLine(user1 == user2); // True
Console.WriteLine(ReferenceEquals(user1, user2)); // False
Console.WriteLine(user1 == user3); // False
Console.WriteLine(user1.Equals(user3)); // False
Console.WriteLine(ValueObject.EqualityComparer.Equals(user1, user3)); // True
总的来说,我有点困惑,限制记录对象的等式方法覆盖是一种预期行为,还是因为它仍处于预览阶段?如果这是设计之意,您会以不同的方式实现上述行为(更好),还是继续使用类?
dotnet --version
输出:5.0.100-rc.1.20452.10
virtual
,要么也取消FullNamVo类型的封印:“记录类型实现了System.IEquatable<R>,并包括一个合成的强类型重载Equals(R? other),其中R是记录类型。该方法是公共的,如果记录类型未被封印,则该方法是虚拟的。* [bool Equals(R? r)]方法可以被明确声明。如果显式声明与预期签名或可访问性不匹配,或者显式声明不允许在派生类型中覆盖它且记录类型未被封印,则会出现错误。*” - user2864740