簡體   English   中英

C# 記錄類型:記錄子類之間的平等比較

[英]C# Record Types: Equality comparisons between record sub-classes

給定父記錄類型:

public record Foo(string Value);

還有兩個記錄子類BarBee我想知道是否可以在基礎 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM