列出自定义的相等性 class

List equality of a custom class

我有一个 class 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();
    }
}

我还有一个 class 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 方法? 有没有办法重用 A 等于?

这样的事情怎么样:

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;
}

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

  1. A.Equals:

如果A没有密封,obj is A ...如果比较不同的类型可以return误报。所以更正后的版本:

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 将为不同但相等的实例 return 不同的哈希码,这是错误的。改为从自身属性派生哈希码。如果 Prop 就像一些 ID,那么只需 return 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 个实例的哈希码。在这里,我使用了一个简单的异或,但如果相同的项目经常以不同的顺序出现,你可以想出更奇特的东西。

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

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>

可以使用 SequenceEquals 方法(来自 System.Linq 命名空间)为列表实现 Equals,这可确保一个列表中的每个项目同时等于该项目另一个列表中的索引。

您可能会考虑更改的一件事是您对 GetHashCode 的实施。如果两个项目相等,此方法应该 return 相同的数字(尽管不能保证具有相同哈希码的两个项目相等)。使用 base.GetHashCode() 不满足此要求,因为在这种情况下 baseobject;根据 the documentation"hash codes for reference types are computed by calling the Object.GetHashCode method of the base class, which computes a hash code based on an object's reference",所以如果对象引用完全相同的对象,则对象仅 return 相同的 HashCode。

HashCode 应该基于用于确定相等性的相同属性,因此在这种情况下我们希望将 Prop.GetHashCode() 用于 class A,并且我们想要为 class B.

聚合 Prop 中所有项目的哈希码

这是重构 classes 的一种方法:

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;
    }
}