在我目前的项目中,我正在尝试比较两个对象列表,找出对象是否已添加,删除,更改或保持不变。
我正在利用IEnumerable.Except
进行如下操作:
Dim newOnes = current.Except(previous, equalityComparer).ToList
Dim removedOnes = previous.Except(current, equalityComparer).ToList()
Dim existingOnes = current.Except(newOnes, equalityComparer).ToList
Dim changedOnes = existingOnes.Except(previous, changedComparer).ToList()
Dim unchangedOnes = existingOnes.Except(changedOnes, equalityComparer).ToList()
为此,我必须实现IEqualityComparers 找出一对对象在属性值(changedOnes)中是否发生了变化,需要我写一个'changedComparer',它是一个IEqualityComparer,用于检查非身份定义字段(例如成员集合)。
由于Except方法显然首先检查GetHashCode,并且如果哈希值相等,则不会转到Equals方法,因此我的设置崩溃了。
我目前正在解决此问题:
Public Overloads Function GetHashCode(obj As Family) As Integer Implements IEqualityComparer(Of Family).GetHashCode
Dim hashCode As Long = 17
If obj.ClientCode IsNot Nothing Then hashCode = CInt(((hashCode * 397) Xor obj.ClientCode.GetHashCode()) Mod Integer.MaxValue)
' SNIP a bunch more property fields
If obj.Members IsNot Nothing Then hashCode = CInt(((hashCode * 397) Xor obj.Members.GetHashCode()) Mod Integer.MaxValue)
Return CInt(hashCode Mod Integer.MaxValue)
End Function
添加“成员”列表的哈希值时,总是会在检查实例时返回不同的哈希值,而不是内容。这暂时适用,但当然会消除哈希的所有优点。
更新
我正在寻找的是不一个更好的Equals方法,但我质疑我的整个方法(可能有一些OOTB,我应该使用不同的接口)。如果不这样做,当我的财产收集应该考虑在内时,我怎么能有一个好的GetHashcode? p>
答案 0 :(得分:1)
我认为Enumerable.SequenceEqual
应该这样做,因为它总是使用默认的相等比较器而不需要实现IEqualityComparer
。但需要注意的是,它还会检查订单比较。
答案 1 :(得分:1)
我使用KeyEqualityComparer<T, TKey>
类,允许我指定一个提取lambda的密钥来检测任何新的或删除的项目。
我已经实现了一个ListDifference
(在C#中,抱歉)实用程序,当给出keyExtractor
和comparer
时会吐出新的,已删除,已更改和未更改的项目列表。它显而易见,即
public ListDifference(IEnumerable<T> original,
IEnumerable<T> updated,
Func<T, object> keyExtractor = null,
Func<T, T, bool> comparer = null)
{
if (keyExtractor == null)
keyExtractor = t => t;
AddedItems = updated.Difference(original, keyExtractor);
RemovedItems = original.Difference(updated, keyExtractor);
var matches = from u in updated
from o in original
where keyExtractor(u).Equals(keyExtractor(o))
select Tuple.Create(o, u);
if (comparer == null)
{
//we are unable to detect changed items
ChangedItems = null;
UnchangedItems = matches;
}
else
{
ChangedItems = matches.Where(t => !comparer(t.Item1, t.Item2));
UnchangedItems = matches.Where(t => comparer(t.Item1, t.Item2));
}
}