找到两个列表之间的差异

时间:2013-12-02 14:08:02

标签: .net vb.net hash equality

在我目前的项目中,我正在尝试比较两个对象列表,找出对象是否已添加,删除,更改或保持不变。

我正在利用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?

2 个答案:

答案 0 :(得分:1)

我认为Enumerable.SequenceEqual应该这样做,因为它总是使用默认的相等比较器而不需要实现IEqualityComparer。但需要注意的是,它还会检查订单比较。

答案 1 :(得分:1)

我使用KeyEqualityComparer<T, TKey>类,允许我指定一个提取lambda的密钥来检测任何新的或删除的项目。

我已经实现了一个ListDifference(在C#中,抱歉)实用程序,当给出keyExtractorcomparer时会吐出新的,已删除,已更改和未更改的项目列表。它显而易见,即

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