[英]C# Record Types: Equality comparisons between record sub-classes
給定父記錄類型:
public record Foo(string Value);
還有兩個記錄子類Bar
和Bee
我想知道是否可以在基礎 class 中實現Equals
,因為 Foo 、 Bar 或 Bee 的實例都被認為是基於Value
相等的(都帶有Equals
和==
)。
在消化https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/records后,我嘗試了以下操作,但效果不佳:
public record Foo(string Value)
{
public virtual bool Equals(Foo? other)
{
return other != null && this.Value == other.Value;
}
public override int GetHashCode() => this.Value.GetHashCode();
}
public record Bar(string Value) : Foo(Value)
{
protected override Type EqualityContract => typeof(Foo);
}
public record Bee(string Value) : Foo(Value)
{
protected override Type EqualityContract => typeof(Foo);
}
[Test]
public void TestFooBar()
{
Assert.That(new Foo("foo") == new Bar("foo"), Is.True); // Passes
Assert.That(new Bar("foo") == new Foo("foo"), Is.True); // Fails!
}
[Test]
public void TestFooBee()
{
Assert.That(new Foo("foo") == new Bee("foo"), Is.True); // Passes
Assert.That(new Bee("foo") == new Foo("foo"), Is.True); // Fails!
}
[Test]
public void TestBarBee()
{
Assert.That(new Bar("foo") == new Bee("foo"), Is.True); // Fails!
Assert.That(new Bee("foo") == new Bar("foo"), Is.True); // Fails!
}
這個問題是特定於記錄類型的。 我不需要類的例子(我已經知道了)。
當我查看sharplab.io時,我看到Bar
的以下實現:
[System.Runtime.CompilerServices.NullableContext(2)]
public override bool Equals(object obj)
{
return Equals(obj as Bar);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public sealed override bool Equals(Foo other)
{
return Equals((object)other);
}
在Foo
中:
[System.Runtime.CompilerServices.NullableContext(2)]
public static bool operator ==(Foo left, Foo right)
{
if ((object)left != right)
{
if ((object)left != null)
{
return left.Equals(right);
}
return false;
}
return true;
}
[System.Runtime.CompilerServices.NullableContext(2)]
public override bool Equals(object obj)
{
return Equals(obj as Foo);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public virtual bool Equals(Foo other)
{
if (other != null)
{
return Value == other.Value;
}
return false;
}
所以很自然 new Bar("foo") == new Bee("foo")
最終會調用Bar.Equals(Foo?)
,它是合成的並且依賴於Bar.Equals(object)
,它將轉換為Bar
或返回null,因為Bee
不是Bar
,所以它最終比較了 null。
似乎無法覆蓋某些合成 Equals 方法,因此我似乎無法逃避這種行為。
或者,我可以嗎?
注意:我在 .NET SDK 6.0 上。
這是調用new Bar("foo") == new Foo("foo")
時的堆棧跟蹤:
在 Foo.Equals(Foo other) 在 Bar.Equals(Bar other) 在 Bar.Equals(Object obj) 在 Bar.Equals(Foo other) 在 Foo.op_Equality(Foo r1, Foo r2)
根據草案規范,除了Foo.Equals(Foo)
和Bar.Equals(Bar)
之外,您不能顯式聲明任何這些方法(即無法控制它們),此時other
已經(未成功)強制轉換為Bar
,在Bar.Equals(Object)
中。
EqualityContract
是相當無關緊要的。 僅在生成的Foo.Equals(Foo)
中檢查,
當且僅當以下各項都為真時,合成的
Equals(R?)
才返回真:
- [...]
- 如果存在基本記錄類型,
base.Equals(other)
的值(對public virtual bool Equals(Base? other)
的非虛擬調用); 否則EqualityContract == other.EqualityContract
的值。
但那時other
已經是 null 了! 更不用說您編寫了自己的Foo.Equals(Foo)
,因此根本不使用EqualityContract
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.