C# 比较两个列表的差异

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/675699/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 12:59:13  来源:igfitidea点击:

Compare two Lists for differences

c#.netlist.net-2.0

提问by

I would like some feedback on how we can best write a generic function that will enable two Lists to be compared. The Lists contain class objects and we would like to iterate through one list, looking for the same item in a second List and report any differences.

我想要一些关于我们如何最好地编写一个通用函数的反馈,该函数可以比较两个列表。列表包含类对象,我们希望遍历一个列表,在第二个列表中查找相同的项目并报告任何差异。

We already have a method to compare classes, so we need feedback on how we can feed the method (shown below) from two Lists.

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

For example, say we have a simple "Employee" class that has three properties, Name, ID, Department. We want to report the differences between List and another List.

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

Note:
Both lists will always contain the same number of items.

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

As mentioned above, we have a generic method that we use to compare two classes, how can we incorporate this method to cater for Lists, i.e. from another method, loop through the List and feed the classes to the generic method .... but how do we find the equivalent class in the second List to pass to the method below;

如上所述,我们有一个用于比较两个类的通用方法,我们如何合并此方法来满足 Lists 的需求,即从另一个方法循环遍历 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;
}

Any suggestions or ideas appreciated?

任何建议或想法表示赞赏?

Edit: Targeting .NET 2.0 so LINQ is out of the question.

编辑:针对 .NET 2.0,所以 LINQ 是不可能的。

采纳答案by Daniel Brückner

.... but how do we find the equivalent class in the second List to pass to the method below;

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

This is your actual problem; you must have at least one immutable property, a id or something like that, to identify corresponding objects in both lists. If you do not have such a property you, cannot solve the problem without errors. You can just try to guess corresponding objects by searching for minimal or logical changes.

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

If you have such an property, the solution becomes really simple.

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

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


thanks to you both danbruc and Noldorin for your feedback. both Lists will be the same length and in the same order. so the method above is close, but can you modify this method to pass the enum.Current to the method i posted above?

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

Now I am confused ... what is the problem with that? Why not just the following?

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

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

The Math.Min() call may even be left out if equal length is guaranted.

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



Noldorin's implementation is of course smarter because of the delegate and the use of enumerators instead of using ICollection.

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

回答by Noldorin

I think you're looking for a method like this:

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

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

It's untested, but it should do the job nonetheless. Note that what's particularly useful about this method is that it's full generic, i.e. it can take two sequences of arbitrary (and different) types and return objects of any type.

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

This solution of course assumes that you want to compare the nth item of seq1with the nth item in seq2. If you want to do match the elements in the two sequences based on a particular property/comparison, then you'll want to perform some sort of joinoperation (as suggested by danbruc using Enumerable.Join. Do let me know if it neither of these approaches is quite what I'm after and maybe I can suggest something else.

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

Edit:Here's an example of how you might use the CompareSequencesmethod with the comparer function you originally posted.

编辑:这是一个示例,说明您可以如何将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());
}

回答by Noldorin

I hope that I am understing your question correctly, but you can do this very quickly with Linq. I'm assuming that universally you will always have an Id property. Just create an interface to ensure this.

我希望我能正确理解你的问题,但你可以用 Linq 很快地做到这一点。我假设您将始终拥有 Id 属性。只需创建一个接口来确保这一点。

If how you identify an object to be the same changes from class to class, I would recommend passing in a delegate that returns true if the two objects have the same persistent id.

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

Here is how to do it in Linq:

以下是在 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);
        }

回答by Artur

This solution produces a result list, that contains all differences from both input lists. You can compare your objects by any property, in my example it is ID. The only restriction is that the lists should be of the same type:

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

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

回答by user2284452

This approach from Microsoft works very well and provides the option to compare one list to another and switch them to get the difference in each. If you are comparing classes simply add your objects to two separate lists and then run the comparison.

Microsoft 的这种方法效果很好,并提供了将一个列表与另一个列表进行比较并切换它们以获取每个列表的差异的选项。如果您正在比较类,只需将您的对象添加到两个单独的列表中,然后运行比较。

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

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