Comparing two collections
i have what seems like a common problem / pattern. two collections of the same object. The object has a number of properties and some nested objects within it. Car has a property called id which is the unique identifier.
I want to find the LINQ way to do a diff, which includes:
- Items in one collection and not the other (visa versa)
- For the items tha开发者_开发技巧t match, are there any changes (changes would be a comparison of all properties? (i only care about settable properties, would i use reflection for this ?? )
You can use the Enumerable.Except()
method. This uses a comparer (either a default or one you supply) to evaluate which objects are in both sequences or just one:
var sequenceA = new[] { "a", "e", "i", "o", "u" };
var sequenceB = new[] { "a", "b", "c" };
var sequenceDiff = sequenceA.Except( sequenceB );
If you want to perform a complete disjunction of both sequences (A-B) union (B-A)
, you would have to use:
var sequenceDiff =
sequenceA.Except( sequenceB ).Union( sequenceB.Except( sequenceA ) );
If you have a complex type, you can write an IComparer<T>
for your type T and use the overload that accepts the comparer.
For the second part of your question, you would need to roll your own implementation to report which properties of a type are different .. there's nothing built into the .NET BCL directly. You have to decide what form this reporting would take? How would you identify and express differences in a complex type? You could certainly use reflection for this ... but if you're only dealing with a single type I would avoid that, and write a specialized differencing utility just for it. If yo're going to support a borad range of types, then reflection may make more sense.
You've already received an excellent answer for your first half. The second half, as LBushkin explains, cannot be done by BCL classes directly. Here's a simple method that goes through all public settable properties (note: it is possible that the gettor, in these cases, is not public!) and compares them one by one. If two objects are 100% equal, it will return true. Else, it will break out early and return false:
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;
}
You could easily add this method to a standard LINQ expression, like this:
var reallyReallyEqual = from itemA in listA
join itemB in listB
on AllSettablePropertiesEqual(itemA, itemB)
select itemA;
精彩评论