比较两个列表的差异

我想就如何最好地编写一个能够比较两个列表的通用函数提供一些反馈。 列表包含类对象,我们想遍历一个列表,在第二个列表中寻找相同的项目,并报告任何差异。

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

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

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

如上所述,我们有一个通用的方法来比较两个类,我们如何结合这个方法来迎合列表,即从另一个方法,通过列表循环,并将类提供给generics方法….但我们如何在第二个List中find等价类来传递给下面的方法;

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

任何build议或想法赞赏?

编辑:面向.NET 2.0所以LINQ是不可能的。

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

这是你的实际问题; 你必须至less有一个不变的属性,一个id或类似的东西,以确定在这两个列表中的对应的对象。 如果你没有这样的财产,就不能无故解决问题。 您可以尝试通过search最小或逻辑更改来猜测相应的对象。

如果你有这样的财产,解决scheme变得非常简单。

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

感谢Danbruc和Noldorin的反馈。 两个列表将是相同的长度和相同的顺序。 所以上面的方法是接近的,但你可以修改这个方法来传递枚举的上述方法?

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

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

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


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

此解决scheme生成一个结果列表,其中包含来自两个input列表的所有差异。 你可以通过任何属性比较你的对象,在我的例子中它是ID。 唯一的限制是列表应该是相同的types:

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

我想你正在寻找一个像这样的方法:

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

这是未经testing的,但它应该做的工作。 请注意,这种方法特别有用的是它是完全通用的,即它可以采用两个任意(不同)types的序列并返回任何types的对象。

当然,这个解决scheme假定你想比较seq1的第n个项目和seq2的第n个项目。 如果你想匹配两个序列中基于特定属性/比较的元素,那么你需要执行某种连接操作(如Danbruc使用Enumerable.Join所build议的那样。如果它不是这些方法是我所追求的,也许我可以build议一些其他的东西。

编辑:这里是一个例子,你可以如何使用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()); } 

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

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

我希望我正确地提出你的问题,但你可以用Linq很快做到这一点。 我假设普遍你将永远有一个Id属性。 只需创build一个接口来确保这一点。

如果你如何识别一个对象是从一个类到另一个类的相同的变化,我build议传入一个委托,如果两个对象具有相同的持久性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); }