列出自定义类的相等性

时间:2019-06-28 15:28:06

标签: c# list comparison equality

我有一个类A,该类拥有一个字符串属性,并覆盖Equals以进行相等性测试。

public class A
{
    public string Prop { get; }

    public A(string val)
    {
        Prop = val;
    }

    public override bool Equals(object obj)
    {
        return obj is A arg && (Prop == arg.Prop);
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

我还有一个类B,其属性为List<A>

public class B
{
    public IReadOnlyList<A> Prop { get; }

    public B(IReadOnlyList<A> val)
    {
        Prop = val;
    }

    public override bool Equals(object obj)
    {
        // ...
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

我想比较一下B实例的相等性和顺序。 如何通过不重写在A中编写的相同代码来在B中编写Equals方法? 有没有办法重用“等于”?

4 个答案:

答案 0 :(得分:2)

更新:我的第一个版本假定为B来自A

  1. A.Equals

如果未密封A,则如果比较不同的类型,obj is A ...可能返回假阳性。因此,更正的版本:

public override bool Equals(object obj)
{
    return obj is A other
        && this.Prop == other.Prop
        && this.GetType() == other.GetType(); // not needed if A is sealed
}
  1. A.GetHashCode

base.GetHashCode将为不同但相等的实例返回不同的哈希码,这是错误的。而是从自身属性派生哈希码。如果Prop的行为类似于某个ID,则只需返回Prop.GetHashCode()

  1. B.Equals
    public override bool Equals(object obj)
    {
        return obj is B other
            && this.Prop.SequenceEqual(other.Prop) // will re-use A.Equals
            && this.Prop.GetType() == other.Prop.GetType() // not needed if different IReadOnlyList types are ok
            && this.GetType() == other.GetType(); // not needed if B is sealed
    }
  1. B.GetHashCode

您可以聚合A实例的哈希码。在这里,我使用一个简单的XOR,但是如果相同的项目通常可以以不同的顺序出现,那么您可以想出一些更奇特的东西。

return Prop.Aggregate(0, (h, i) => h ^ i.GetHashCode());

答案 1 :(得分:1)

Linq包含一种比较集合的有用方法:SequenceEqual

public override bool Equals(object obj)
{
    if (!(obj is B other))
    {
        return false;
    }

    if (this.Prop == null || other.Prop == null)
    {
        return false;
    }

    return this.Prop.SequenceEqual(other.Prop);
}

此外,当您覆盖Equals时,请实施IEquatable<T>

答案 2 :(得分:1)

通过使用Equals方法(来自SequenceEquals名称空间)可以为列表添加System.Linq,这可以确保一个列表中的每个项目等于列表中相同索引处的项目。另一个列表。

您可能会考虑更改的一件事是GetHashCode的实现。如果两个项目相等,则此方法应返回相同的数字(尽管不能保证具有相同哈希码的两个项目相等)。使用base.GetHashCode()不符合此要求,因为在这种情况下baseobject;根据{{​​3}},“用于引用类型的哈希码是通过调用基类的Object.GetHashCode方法来计算的,该方法基于对象的引用来计算哈希码” ,因此仅对象如果它们引用完全相同的对象,则返回相同的HashCode。

HashCode应该基于用于确定相等性的相同属性,因此在这种情况下,我们要对类Prop.GetHashCode()使用A,并希望为PropB类的所有项目。

这是可以重构类的一种方法:

public class A : IEquatable<A>
{
    public string Prop { get; }

    public A(string val)
    {
        Prop = val;
    }

    public bool Equals(A other)
    {
        if (other == null) return false;
        return Prop == other.Prop;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as A);
    }

    public override int GetHashCode()
    {
        return Prop.GetHashCode();
    }
}

public class B : IEquatable<B>
{
    public IReadOnlyList<A> Prop { get; }

    public B(IReadOnlyList<A> val)
    {
        Prop = val;
    }

    public bool Equals(B other)
    {
        if (other == null) return false;
        if (ReferenceEquals(this, other)) return true;
        if (Prop == null) return other.Prop == null;
        return other.Prop != null && Prop.SequenceEqual(other.Prop);
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as B);
    }

    public override int GetHashCode()
    {
        return Prop?.Aggregate(17,
            (current, item) => current * 17 + item?.GetHashCode() ?? 0)
                ?? 0;
    }
}

答案 3 :(得分:0)

怎么样呢?

public override bool Equals(object obj)
{
    if(!(obj is B))
    {
        return false;
    }

    var b = obj as B;

    if(b.Prop.Count != this.Prop.Count)
    {
        return false;
    }

    for(var i =0; i < Prop.Count; i++)
    {
        if (!Prop.ElementAt(i).Equals(b.Prop.ElementAt(i)))
        {
            return false;
        }
    }

    return true;
}
相关问题