IEnumerable.Except() 如何运作?
How IEnumerable.Except() works?
我正在尝试排除要添加到数据库中的实体(如果它们已经存在于数据库中)。所以我决定 newBillInstances.Except(dbContext.BillInstances)
是最好的方法。然而,它根本不起作用(没有实体被排除在外),尽管 List<string>
它工作得很好。
我读了this discussion and actual decription of .Except()
in MSDN。它声明要在 .Except()
中使用的 class 应该实现 IEqualityComparer<T>
以使用默认比较器。
其实MSDN的文章并没有完整的描述比较两个实例的过程。我仍然不明白为什么 Equals() 和 GetHashObject() 都必须被覆盖。
我已经实现了 IEqualityComparer<BillInstance>
接口并在两个方法中都设置了断点,但是在调用 .Except(IEnumerable)
时它没有被使用。只有当我更改为 .Except(IEnumerable, new BillInstanceComparer())
时,我才在 GetHashCode()
中咳嗽,但在 Equals()
中没有咳嗽。
然后我在 BillInstance
class 中实现了 IEqualityComparer<BillInstance>
并预计它会在使用 .Except(IEnumerable)
时使用,但两种方法都没有中断。
所以我有两个问题:
- 使用
.Except(IEnumerable)
需要做什么?
- 为什么根本不用
Equals()
?是否只在两个实例的哈希码相同的情况下使用?
因为Equals()
仅在两个对象具有相同的GetHashCode()
时使用。如果没有对象具有相同的 GetHashCode()
那么就没有机会使用 Equals()
.
内部Except()
使用Set<>
(你可以看到它here), that is an internal class that you should consider to be equivalent to HashSet<>
。这个class使用对象的散列来"index"它们,然后使用 Equals()
检查具有相同哈希值的两个对象是否相同或不同,但具有相同的哈希值。
Link 其他相关回答:
代码中某处隐藏了一个集合或一个map/dictionary。
这些家伙通常包含许多桶,这些桶随着集合中存储的元素数量而增长。一个元素根据哈希码被划分到桶中,桶内的实际身份比较是使用 equals 完成的。
因此哈希码用于查找正确的存储桶(为什么需要 GetHashCode),然后使用 equals 将其与存储桶中的其他元素进行比较。
这就是您需要同时实施两者的原因。
好的,从 IEnumerable 来源 () 我已经了解调用 Except(IEnumerable)
:
的内部原理
enumerable1.Except(enumerable2)
调用 ExceptIterator(enumerable1, enumerable2, null)
,其中 null
应该是 IEquitableComparer
的实例。
ExceptIterator()
创建一个内部实例 class Set
传递 null
作为比较器。
由于比较器是 null
,因此使用 属性 EqualityComparer<TElement>.Default
。
Default
属性 为 TElement
创建一个比较器,除非它已经通过调用 CreateComparer()
创建。具体有 2 点对我来说很有趣:
如果 TElement
实现了 IEquatable
接口,那么据我所知,一些 IEquatable
的通用比较器被创建。我相信它会使用 IEquatable.GetHashCode()
和 IEquatable.Equals()
.
对于一般情况(不是字节类型,没有实现 IEquatable,不是 Nullable,不是枚举)返回 ObjectEqualityComparer
实例。 ObjectEqualityComparer.GetHashCode()
和ObjectEqualityComparer.Equals()
一般调用TElement
.
对应的方法
所以这让我理解了我的情况(BillInstance
的每个实例通常是不可变的)它应该足以覆盖 Object.GetHashCode()
和 Object.Equals()
.
我正在尝试排除要添加到数据库中的实体(如果它们已经存在于数据库中)。所以我决定 newBillInstances.Except(dbContext.BillInstances)
是最好的方法。然而,它根本不起作用(没有实体被排除在外),尽管 List<string>
它工作得很好。
我读了this discussion and actual decription of .Except()
in MSDN。它声明要在 .Except()
中使用的 class 应该实现 IEqualityComparer<T>
以使用默认比较器。
其实MSDN的文章并没有完整的描述比较两个实例的过程。我仍然不明白为什么 Equals() 和 GetHashObject() 都必须被覆盖。
我已经实现了 IEqualityComparer<BillInstance>
接口并在两个方法中都设置了断点,但是在调用 .Except(IEnumerable)
时它没有被使用。只有当我更改为 .Except(IEnumerable, new BillInstanceComparer())
时,我才在 GetHashCode()
中咳嗽,但在 Equals()
中没有咳嗽。
然后我在 BillInstance
class 中实现了 IEqualityComparer<BillInstance>
并预计它会在使用 .Except(IEnumerable)
时使用,但两种方法都没有中断。
所以我有两个问题:
- 使用
.Except(IEnumerable)
需要做什么? - 为什么根本不用
Equals()
?是否只在两个实例的哈希码相同的情况下使用?
因为Equals()
仅在两个对象具有相同的GetHashCode()
时使用。如果没有对象具有相同的 GetHashCode()
那么就没有机会使用 Equals()
.
内部Except()
使用Set<>
(你可以看到它here), that is an internal class that you should consider to be equivalent to HashSet<>
。这个class使用对象的散列来"index"它们,然后使用 Equals()
检查具有相同哈希值的两个对象是否相同或不同,但具有相同的哈希值。
Link 其他相关回答:
代码中某处隐藏了一个集合或一个map/dictionary。
这些家伙通常包含许多桶,这些桶随着集合中存储的元素数量而增长。一个元素根据哈希码被划分到桶中,桶内的实际身份比较是使用 equals 完成的。
因此哈希码用于查找正确的存储桶(为什么需要 GetHashCode),然后使用 equals 将其与存储桶中的其他元素进行比较。
这就是您需要同时实施两者的原因。
好的,从 IEnumerable 来源 (Except(IEnumerable)
:
enumerable1.Except(enumerable2)
调用ExceptIterator(enumerable1, enumerable2, null)
,其中null
应该是IEquitableComparer
的实例。ExceptIterator()
创建一个内部实例 classSet
传递null
作为比较器。由于比较器是
null
,因此使用 属性EqualityComparer<TElement>.Default
。Default
属性 为TElement
创建一个比较器,除非它已经通过调用CreateComparer()
创建。具体有 2 点对我来说很有趣:如果
TElement
实现了IEquatable
接口,那么据我所知,一些IEquatable
的通用比较器被创建。我相信它会使用IEquatable.GetHashCode()
和IEquatable.Equals()
.对于一般情况(不是字节类型,没有实现 IEquatable,不是 Nullable,不是枚举)返回
ObjectEqualityComparer
实例。ObjectEqualityComparer.GetHashCode()
和ObjectEqualityComparer.Equals()
一般调用TElement
. 对应的方法
所以这让我理解了我的情况(BillInstance
的每个实例通常是不可变的)它应该足以覆盖 Object.GetHashCode()
和 Object.Equals()
.