如何在 [duplicate] 的类中实现 IEquatable<MyType>

How to Implement IEquatable<MyType> in a class that [duplicate]

提问人:Eric Snyder 提问时间:12/2/2021 更新时间:12/2/2021 访问量:168

问:

我需要让继承自 DispenseEntity 的 DispenseFile 类的实例实现 IDispenseEntity 使用自定义相等来比较 List 中的元素。

我的界面是:

public interface IDispenseEntity : IEquatable<IDispenseEntity>
{
    byte[] Id { get; set; }
    List<byte[]> Key { get; }
    List<byte[]> ParentKey { get; set; }
    double Volume { get; set; }
    public bool DispenseEnabled { get; set; }
    string Name { get; set; }
}

我的 DispenseEntity 类:

public class DispenseEntity : IDispenseEntity, IEquatable<IDispenseEntity>
{
    //Other properties and methods
    
    //I've tried - this implements IEquatable:
    public bool Equals(IDispenseEntity other)
    {
        return Id.SequenceEqual(other.Id);
    }

    //I've tried - this overrides default:
    public override bool Equals(object obj)
    {
        return Id.SequenceEqual((obj as IDispenseEntity).Id);
    }
}

我的 DispenseFile 类:

public class DispenseFile : DispenseEntity, IParent, IOutputable, IDispenseFile
{
    //Other methods and properties
    
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
}

无论我在 Equals() 方法的 DispenseEntity 类中使用什么,当我尝试时,它都不会被使用:

List<IDispenseEntity> before = _aList;
List<IDispenseEntity> after = _bList;
var intersect = before.Intersect(after).ToList();

相交列表的元素为零。

我绝对肯定 _aList 和 _bList都有一个 DispenseFile 实例,该实例继承了实现 IDispenseEntity 的 DispenseEntity。我编写了测试代码,用于查找 _aList 中唯一的 DispenseFile 实体,并在 _bList 中查找 DispenseFile 的单个实例。这两个实例是单独创建的,并且具有相同的属性 ID (new byte[] {1,2,3,4} )

我试过覆盖 Equals。我尝试将 IEquatable 添加到基类并实现 equals 和 GetHashCode,但这些都没有被使用。

问题一定是我,我做错了什么?

C# LINQ 易等式

评论

2赞 Jon Skeet 12/2/2021
GetHashCode 绝对被使用,如果你要覆盖 Equals,你绝对应该覆盖它。请提供一个最小的可重现示例,这样会更容易帮助您。
0赞 Caius Jard 12/2/2021
Intersect 在内部使用 HashSet;您必须重写 GetHashCode,因为它用于首次检测列表 A 中的集合是否包含列表 B 中的项。如果坚持使用默认的 GetHashcode,则永远不会成功与不同的实例相交
0赞 Jon Skeet 12/2/2021
另请注意,类实现 .您通常应该针对相同的类型实现(即 应实现IEquatable<ISomeInterface>IEquatableDispenseEntityIEquatable<DispenseEntity>)

答:

1赞 Caius Jard 12/2/2021 #1

您甚至不需要实现 IEquatable 任何东西;重写 GetHashCode 和 Equals 就足够了

public class DispenseEntity : IDispenseEntity
{
    ...

    public override int GetHashCode()
    {
        return new BigInteger(Id).GetHashCode(); //or your choice
    }

   
    public override bool Equals(object obj)
    {
        return obj is DispenseEntity x && x.Id.SequenceEqual(Id);
    }
}

无论我在 DispenseEntity 类中使用什么 Equals() 方法,它都不会被使用

事实上,它可能不是;关于相交的摘要:

Intersect 使用 HashSet;将列表 B 的内容添加到集合中,然后枚举 A 的内容并尝试将其添加到集合中。如果 Add 返回 false,表示该项已为已知,则生成 A 中的项。在操作结束时,A 中所有也在 B 中的项目都已返回

现在,对于使用默认哈希码提供程序的 HashSet,为了确定它是否包含某个对象 X,它首先获取 X 的哈希码,并查看它所知道的对象,看看是否有任何其他具有相同哈希码的对象。

如果没有具有相同哈希值的已知对象,则认为该集不包含该对象。

如果存在具有相同哈希值的对象,则它使用 Equals 来确定碰撞对象是否确实相同。如果您依赖于 object 的默认哈希码实现,它本质上是项目的内存地址,因此获得相同哈希码的唯一方法是列表 A 和列表 B 共享一个实例

长话短说,如果你不重写 GetHashCode,你会得到一个空的集合结果,因为当所有 of 被添加到集合中时,然后所有 of 都被枚举出来,并且该集合被问到“得到这个了吗?”,答案总是“否” - Equals 永远不需要用于确定实例是否相同,因为哈希值总是不同的, 而交点是(无)_bList_aList{ }

..但是,如果您重写了 Equals 和 GetHashCode,您应该可以开始了。你甚至可以将 GetHashCode 重写为(不要;这将是非常低效的),你会看到 Equals 被使用(很多)。return 1

评论

0赞 Eric Snyder 12/2/2021
这似乎是一个非常好的解释。非常感谢你。我将确保重写 Equals 和 GetHashCode,看看会发生什么。
0赞 Caius Jard 12/2/2021
“对于有效的比较,就像你一样,他们不会这样做;重写 Equals 时,也要执行 GetHashCode”
0赞 Eric Snyder 12/3/2021
哇,编程天才和诗人!
0赞 Caius Jard 12/3/2021
哦,我不知道我是否会走那么远,但我感谢您的赞美!:)