C#9记录的自定义相等性检查

C#9记录的自定义相等性检查,c#,record,equality,c#-9.0,C#,Record,Equality,C# 9.0,据我所知,记录实际上是类,它们以一种方式实现自己的相等性检查,即对象是值驱动的,而不是引用驱动的 简而言之,对于这样实现的record Foo:var Foo=new Foo{Value=“Foo”}和var bar=new Foo{Value=“Foo”},Foo==bar表达式将导致True,即使它们具有不同的引用(ReferenceEquals(Foo,bar)//False) 现在有了记录,尽管在.Net博客上有这样的记录,但它说: 如果您不喜欢的默认逐字段比较行为 生成的Equals重

据我所知,记录实际上是类,它们以一种方式实现自己的相等性检查,即对象是值驱动的,而不是引用驱动的

简而言之,对于这样实现的
record Foo
var Foo=new Foo{Value=“Foo”}
var bar=new Foo{Value=“Foo”}
Foo==bar
表达式将导致
True
,即使它们具有不同的引用(
ReferenceEquals(Foo,bar)//False

现在有了记录,尽管在.Net博客上有这样的记录,但它说:

如果您不喜欢的默认逐字段比较行为 生成的Equals重写,您可以编写自己的

当我试图放置
公共覆盖bool Equals
,或
公共覆盖int GetHashCode
,或
公共静态bool操作符==
,等等。我得到的
成员具有相同的签名已声明为
错误,因此我认为这是一种受限行为,
struct
对象的情况并非如此

:

公共密封记录SimpleVo
:足够
{
公共布尔等于(简单其他)=>
抛出新系统。NotImplementedException();
公共覆盖布尔等于(对象对象对象)=>
obj是简单的其他&&Equals(其他);
公共覆盖int GetHashCode()=>
抛出新系统。NotImplementedException();
公共静态布尔运算符==(SimpleVo左、SimpleVo右)=>
左。等于(右);
公共静态布尔运算符!=(SimpleVo左、SimpleVo右)=>
!左。等于(右);
}
编译器结果:

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
抽象类:

公共密封类FullNameVo:ValueObject
{
公共FullNameVo(字符串名称、字符串姓氏)
{
名称=名称;
姓=姓;
}
[忽略成员]
公共字符串名称{get;}
公共字符串姓氏{get;}
[忽略成员]
公共字符串FullName=>$“{Name}{lasname}”;
}
然后您将得到以下结果:

var user1=newfullnamevo(“John”、“Doe”);
var user2=新的全名VO(“约翰”、“Doe”);
var user3=新的FullNameVo(“Jane”、“Doe”);
Console.WriteLine(user1==user2);//真的
Console.WriteLine(ReferenceEquals(user1,user2));//假的
Console.WriteLine(user1==user3);//真的
Console.WriteLine(user1.Equals(user3));//真的
到目前为止,为了以某种方式实现上述用例,我已经像这样实施和使用了它:

公共密封记录FullNameVo:ValueObject
{
[忽略成员]
公共字符串名称;
公共字符串姓氏;
[忽略成员]
公共字符串FullName=>$“{Name}{lasname}”;
}
结果如下所示:

var user1=new FullNameVo
{
Name=“John”,
姓氏=“能源部”
};
var user2=新的FullNameVo
{
Name=“John”,
姓氏=“能源部”
};
var user3=user1和{Name=“Jane”};
Console.WriteLine(user1==user2);//真的
Console.WriteLine(ReferenceEquals(user1,user2));//假的
Console.WriteLine(user1==user3);//假的
Console.WriteLine(user1.Equals(user3));//假的
Console.WriteLine(ValueObject.EqualityComparer.Equals(user1,user3));//真的
总而言之,我有点困惑,限制重写记录对象的相等方法是一种预期行为,还是因为它仍处于预览阶段?如果是设计的,您是以不同(更好)的方式实现上述行为,还是继续使用类

dotnet--version
输出:
5.0.100-rc.1.20452.10
根据,应编译以下内容,即使在没有实际实现的情况下不是很有用

// No explicit IEquatable<R> - this is synthesized!
public sealed record SimpleVo
{
    // Not virtual, as SimpleVo (R) is sealed.
    // Accepts SimpleVo? (R?), and not SimpleVo (R), as argument.
    public bool Equals(SimpleVo? other) =>
        throw new System.NotImplementedException();

    // Optional: warning generated if not supplied when Equals(R?) is user-defined.
    public int GetHashCode() =>
        throw new System.NotImplementedException();

    // No other “standard” equality members!
}
//没有显式的IEquatable-这是合成的!
公共密封记录SimpleVo
{
//不是虚拟的,因为SimpleVo(R)是密封的。
//接受SimpleVo?(R?)而不是SimpleVo(R)作为参数。
公共布尔等于(SimpleVo?其他)=>
抛出新系统。NotImplementedException();
//可选:当用户定义等于(R?)时,如果未提供,则生成警告。
public int GetHashCode()=>
抛出新系统。NotImplementedException();
//没有其他“标准”平等成员!
}
由于大多数代码都是合成的,因此与平等相关的成员受到限制。该提案包括预期综合基础类型的示例

也就是说,如果a
等于(R?
,编译器将创建一个
==
=
等于(对象)
。 可以通过搜索建议书中的“用户定义”找到可定义的方法

尝试重写/定义其他相等方法或运算符可能会失败:

如果显式声明重写,则为错误

该行为在“平等成员”中进行了讨论,并在以下段落中进行了总结:

记录类型实现了
System.IEquatable
,包括一个合成的强类型重载
book Equals(R?other)
,其中R是记录类型。该方法是公共的,并且除非记录类型是密封的,否则该方法是虚拟的。[
Equals(R?
]方法可以显式声明。如果显式声明与预期的签名或可访问性不匹配,或者显式声明不允许在派生类型a中重写它,则为错误