问题描述
据我了解,记录实际上是类,它们以对象为值驱动而不是引用为驱动的方式实现自己的相等性检查。
简而言之,对于@bot.command()
async def foo(ctx,user: discord.User):
print(user.id)
和record Foo
这样实现的var foo = new Foo { Value = "foo" }
,var bar = new Foo { Value = "foo" }
表达式将得出foo == bar
,即使它们有不同的引用(True
)。
现在有了记录,即使.Net Blog中发布的the article中的记录显示为:
如果您不喜欢默认的逐域比较行为 生成的Equals替代,您可以编写自己的替代。
当我尝试放置ReferenceEquals(foo,bar) // False
或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);
}
我的主要问题是,如果我们要自定义平等检查器的工作方式,该怎么办?我的意思是,我的确知道这超出了记录的全部目的,但另一方面,平等检查器并不是使记录使用起来很酷的唯一功能。
一个人想要覆盖记录相等性的用例是,因为您可能有一个attribute,该属性将从相等性检查中排除某个属性。以this 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
然后您将获得以下results:
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}";
}
到目前为止,为了实现上述用例,我已经实现了an abstract record object并像这样利用它:
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
解决方法
对于每个the C#9 record proposal,以下应该编译,即使没有实际的实现也不是很有用。.
// 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!
}
由于大多数代码是合成的,所以与相等性有关的成员受到限制。该提案包括预期的综合基础类型的示例。
也就是说,仅给{strong> 一个Equals(R?)
,编译器就会创建一个==
,!=
和Equals(object)
。
可以通过在提案中搜索“用户定义”来找到可以定义的方法。
试图尝试覆盖/定义其他相等方法或运算符会失败:
如果显式声明了覆盖,则会出错。
该行为在“平等成员”中进行了讨论,并在以下段落中进行了总结:
记录类型实现
System.IEquatable<R>
,并包括book Equals(R? other)
的综合强类型重载,其中R是记录类型。该方法是公共的,除非密封记录类型,否则该方法是虚拟的。 可以{[Equals(R?)
]方法进行显式声明。如果显式声明与预期的签名或可访问性不匹配,或者显式声明不允许将其覆盖,则错误。派生类型和记录类型未密封。 如果Equals(R? other)
是用户定义的(不是合成的),但是GetHashCode
不是[用户定义的],则会产生警告。