我希望得到一些反馈,说明如何最好地编写一个通用函数,以便比较两个列表。列表包含类对象,我们想迭代一个列表,在第二个列表中查找相同的项目并报告任何差异。
我们已经有了比较类的方法,所以我们需要反馈我们如何从两个列表中提供方法(如下所示)。
例如,假设我们有一个简单的“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是不可能的。
答案 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的这种方法非常有效,并提供了将一个列表与另一个列表进行比较的选项,并将它们切换为每个列表中的差异。如果要比较类,只需将对象添加到两个单独的列表中,然后运行比较。
答案 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);
}