比较两个列表的差异

时间:2009-03-24 00:10:05

标签: c# .net list .net-2.0

我希望得到一些反馈,说明如何最好地编写一个通用函数,以便比较两个列表。列表包含类对象,我们想迭代一个列表,在第二个列表中查找相同的项目并报告任何差异。

我们已经有了比较类的方法,所以我们需要反馈我们如何从两个列表中提供方法(如下所示)。

例如,假设我们有一个简单的“Employee”类,它有三个属性,Name,ID,Department。我们想报告List和另一个List之间的差异。

注意:
两个列表将始终包含相同数量的项目。

如上所述,我们有一个泛型方法,我们用它来比较两个类,我们如何结合这个方法来满足列表,即从另一个方法,循环通过List并将类提供给泛型方法。 ..但是我们如何找到第二个List中的等价类来传递给下面的方法;

public static string CompareTwoClass_ReturnDifferences<T1, T2>(T1 Orig, T2 Dest)
    where T1 : class
    where T2 : class
{
    // Instantiate if necessary
    if (Dest == null) throw new ArgumentNullException("Dest", "Destination class must first be instantiated.");

    var Differences = CoreFormat.StringNoCharacters;

    // Loop through each property in the destination  
    foreach (var DestProp in Dest.GetType().GetProperties())
    {
        // Find the matching property in the Orig class and compare
        foreach (var OrigProp in Orig.GetType().GetProperties())
        {

            if (OrigProp.Name != DestProp.Name || OrigProp.PropertyType != DestProp.PropertyType) continue;
            if (OrigProp.GetValue(Orig, null).ToString() != DestProp.GetValue(Dest, null).ToString())
                Differences = Differences == CoreFormat.StringNoCharacters 
                    ? string.Format("{0}: {1} -> {2}", OrigProp.Name,
                                                       OrigProp.GetValue(Orig, null),
                                                       DestProp.GetValue(Dest, null)) 
                    : string.Format("{0} {1}{2}: {3} -> {4}", Differences,
                                                              Environment.NewLine,
                                                              OrigProp.Name,
                                                              OrigProp.GetValue(Orig, null),
                                                              DestProp.GetValue(Dest, null));
        }
    }
    return Differences;
}

有任何建议或想法受到赞赏吗?

编辑:定位.NET 2.0,因此LINQ是不可能的。

5 个答案:

答案 0 :(得分:72)

此解决方案生成一个结果列表,其中包含两个输入列表的所有差异。 您可以通过任何属性比较您的对象,在我的示例中它是ID。 唯一的限制是列表应该是相同的类型:

var DifferencesList = ListA.Where(x => !ListB.Any(x1 => x1.id == x.id))
            .Union(ListB.Where(x => !ListA.Any(x1 => x1.id == x.id)));

答案 1 :(得分:15)

  

....但是我们如何找到第二个List中的等价类来传递给方法   下面;

这是你的实际问题;你必须至少有一个不可变属性,一个id或类似的东西,以识别两个列表中的相应对象。如果你没有这样的属性,无法解决问题而不会出错。您可以尝试通过搜索最小或逻辑更改来猜测相应的对象。

如果你有这样的属性,解决方案变得非常简单。

Enumerable.Join(
   listA, listB,
   a => a.Id, b => b.Id,
   (a, b) => CompareTwoClass_ReturnDifferences(a, b))

  

感谢danbruc和Noldorin的反馈。两个列表都是一样的   长度和顺序相同。所以上面的方法很接近,但是你可以修改它   将enum.Current传递给上面发布的方法的方法?

现在我很困惑......问题是什么?为什么不只是以下?

for (Int32 i = 0; i < Math.Min(listA.Count, listB.Count); i++)
{
    yield return CompareTwoClass_ReturnDifferences(listA[i], listB[i]);
}

如果保证等长,可以省略Math.Min()调用。


Noldorin的实现当然更聪明,因为委托和使用枚举器而不是使用ICollection。

答案 2 :(得分:6)

我认为你正在寻找这样的方法:

public static IEnumerable<TResult> CompareSequences<T1, T2, TResult>(IEnumerable<T1> seq1,
    IEnumerable<T2> seq2, Func<T1, T2, TResult> comparer)
{
    var enum1 = seq1.GetEnumerator();
    var enum2 = seq2.GetEnumerator();

    while (enum1.MoveNext() && enum2.MoveNext())
    {
        yield return comparer(enum1.Current, enum2.Current);
    }
}

这是未经测试的,但它应该可以完成这项工作。请注意,对于此方法特别有用的是它是完全通用的,即它可以采用任意(和不同)类型的两个序列并返回任何类型的对象。

此解决方案当然假定您要将seq1的第n项与seq2中的第n项进行比较。如果你想根据特定的属性/比较匹配两个序列中的元素,那么你将需要执行某种join操作(正如danbruc使用Enumerable.Join所建议的那样。我知道如果这些方法都不是我所追求的,也许我可以提出别的建议。

修改 以下是如何将CompareSequences方法与最初发布的比较器函数一起使用的示例。

// Prints out to the console all the results returned by the comparer function (CompareTwoClass_ReturnDifferences in this case).
var results = CompareSequences(list1, list2, CompareTwoClass_ReturnDifferences);
int index;    

foreach(var element in results)
{
    Console.WriteLine("{0:#000} {1}", index++, element.ToString());
}

答案 3 :(得分:2)

Microsoft的这种方法非常有效,并提供了将一个列表与另一个列表进行比较的选项,并将它们切换为每个列表中的差异。如果要比较类,只需将对象添加到两个单独的列表中,然后运行比较。

http://msdn.microsoft.com/en-us/library/bb397894.aspx

答案 4 :(得分:1)

我希望我能正确地解决你的问题,但是你可以用Linq快速完成这个问题。我假设普遍存在Id属性。只需创建一个接口即可确保这一点。

如果你如何识别一个对象是从一个类到另一个类的相同变化,我建议传入一个委托,如果这两个对象具有相同的持久id,则返回true。

以下是如何在Linq中执行此操作:

List<Employee> listA = new List<Employee>();
        List<Employee> listB = new List<Employee>();

        listA.Add(new Employee() { Id = 1, Name = "Bill" });
        listA.Add(new Employee() { Id = 2, Name = "Ted" });

        listB.Add(new Employee() { Id = 1, Name = "Bill Sr." });
        listB.Add(new Employee() { Id = 3, Name = "Jim" });

        var identicalQuery = from employeeA in listA
                             join employeeB in listB on employeeA.Id equals employeeB.Id
                             select new { EmployeeA = employeeA, EmployeeB = employeeB };

        foreach (var queryResult in identicalQuery)
        {
            Console.WriteLine(queryResult.EmployeeA.Name);
            Console.WriteLine(queryResult.EmployeeB.Name);
        }