【发布时间】:2021-01-27 06:40:14
【问题描述】:
据我了解,记录实际上是实现自己的相等检查的类,其方式是您的对象是值驱动而不是引用驱动。
简而言之,对于像这样实现的record Foo:var foo = new Foo { Value = "foo" } 和var bar = new Foo { Value = "foo" },foo == bar 表达式将导致True,即使它们具有不同的引用 (ReferenceEquals(foo, bar) // False)。
现在有了记录,尽管the article 发布在 .Net 博客中,它说:
如果您不喜欢默认的逐字段比较行为 生成的 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
我的主要问题是,如果我们想自定义相等检查器的工作方式怎么办?我的意思是,我明白这超出了记录的全部目的,但另一方面,平等检查器并不是让记录使用起来很酷的唯一功能。
有人想要覆盖记录的相等性的一个用例是因为您可能有一个 attribute 会从相等性检查中排除一个属性。以 this 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}";
}
那么你会得到以下results:
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
到目前为止,为了以某种方式实现上述用例,我已经实现了an abstract record object并像这样使用它:
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
【问题讨论】:
-
您使用的是哪个版本的 C# 9 编译器?我确实注意到 C# 9.0 仍处于预览阶段(据我所知),因此某些功能可能尚不可用。
-
@Dai 你是对的朋友!我错过了提及该信息!我现在将更新我的问题。仅供参考:5.0.100-rc.1.20452.10
-
@Dai,补充一下,是的,我知道它仍在开发中,如果它不在 RC1 中,我不会问这个问题,所以作为发布候选版本,我很友善感到困惑的是这是设计使然还是尚未实施。 :)
-
粘贴您的实际 Equals 方法。我刚试过,它奏效了。
-
@JeremyThompson 是的,您可以毫无问题地使用类和结构来做到这一点:dotnetfiddle.net/Widget/apnl6x 到目前为止,我只能用记录来做到这一点(这可能是我的错,因为记录可能需要一种不同的方法).