比较两个集合

时间:2010-08-04 15:54:23

标签: linq collections

我有一个看似常见的问题/模式。同一个对象的两个集合。该对象具有许多属性和一些嵌套对象。 Car有一个名为id的属性,它是唯一的标识符。

我想找到LINQ方式来做一个diff,其中包括:

  1. 一个集合中的项目而不是另一个集合(反之亦然)
  2. 对于匹配的项目,是否有任何更改(更改将是所有属性的比较?(我只关心可设置的属性,我会使用反射吗?)

2 个答案:

答案 0 :(得分:10)

您可以使用Enumerable.Except()方法。这使用比较器(默认值或您提供的比较器)来评估两个序列中的对象或只有一个:

var sequenceA = new[] { "a", "e", "i", "o", "u" };
var sequenceB = new[] { "a", "b", "c" };

var sequenceDiff = sequenceA.Except( sequenceB );

如果要完成两个序列(A-B) union (B-A)的完全分离,则必须使用:

var sequenceDiff = 
         sequenceA.Except( sequenceB ).Union( sequenceB.Except( sequenceA ) );

如果您有复杂类型,可以为类型T编写IComparer<T>并使用接受比较器的重载。

对于问题的第二部分,您需要滚动自己的实现来报告类型的哪些属性不同。直接在.NET BCL中没有内置任何内容。您必须决定此报告采用何种形式?您如何识别和表达复杂类型的差异?你当然可以使用反射...但如果你只处理一种类型我会避免这种情况,并为它编写一个专门的差异实用程序。如果你要支持borad范围的类型,那么反思可能更有意义。

答案 1 :(得分:2)

你的上半场已经收到了很好的答案。正如LBushkin解释的那样,下半场不能直接由BCL课程完成。这是一个简单的方法,遍历所有公共可设置的属性(注意:在这些情况下,gettor可能不公开!)并逐个比较它们。如果两个对象100%相等,则返回true。否则,它会提前爆发并返回错误:

static bool AllSettablePropertiesEqual<T>(T obj1, T obj2)
{
    PropertyInfo[] info1 = obj1.GetType().GetProperties(
        BindingFlags.Public |
        BindingFlags.SetProperty |
        BindingFlags.Instance);      // get public properties

    PropertyInfo[] info2 = obj2.GetType().GetProperties(
        BindingFlags.Public |
        BindingFlags.SetProperty |
        BindingFlags.Instance);      // get public properties

    // a loop is easier than linq here, and we can break out quick:
    for (var i = 0; i < info1.Length; i++)
    {
        var value1 = info1[i].GetValue(obj1, null);
        var value2 = info2[i].GetValue(obj2, null)
        if(value1 == null || value2 ==null)
        {
            if(value1 != value2)
                return false;
        }
        else if (!value1.Equals(value2))
        {
            return false;
        }
    }
    return true;
}

您可以轻松地将此方法添加到标准LINQ表达式中,如下所示:

var reallyReallyEqual = from itemA in listA
                        join itemB in listB 
                          on AllSettablePropertiesEqual(itemA, itemB)
                        select itemA;