LINQ, которая, Кроме с пользовательским компаратор IEqualityComparer
Я пытаюсь найти разницу между двумя универсальными списками, как в примере ниже. Хотя t1 и t2 содержат одинаковые свойства, они не являются одним и тем же объектом, поэтому мне нужно реализовать IEqualityComparer.
Похоже, что это работает с этим примером, но реальный класс имеет несколько других свойств, и мне также нужно сделать то же самое с несколькими другими классами.
Итак, мне было интересно, не изобретаю ли я заново колесо?Есть ли более простой метод сравнение всех свойств двух объектов? На данный момент мне действительно нужно только справиться с классом, содержащим простые типы, но было бы неплохо, чтобы у меня был компаратор, который работал с классами, содержащими экземпляры других классов.
void Main()
{
var t1 = new Sizes { Name = "Test" , Size = 1} ;
var t2 = new Sizes { Name = "Test" , Size = 1} ;
var list1 = new List<Sizes>();
var list2 = new List<Sizes>();
list1.Add(t1);
list2.Add(t2);
var differences = list2.Except(list1 , new SizesComparer());
// differences should be empty.
}
public class Sizes
{
public string Name { get; set; }
public int Size { get; set; }
}
public class SizesComparer : IEqualityComparer<Sizes>
{
bool IEqualityComparer<Sizes>.Equals(Sizes x, Sizes y)
{
return (x.Name.Equals(y.Name) && x.Size.Equals(y.Size));
}
int IEqualityComparer<Sizes>.GetHashCode(Sizes obj)
{
if (Object.ReferenceEquals(obj, null))
return 0;
return obj.Name.GetHashCode() + obj.Size;
}
}
3 ответа:
Вы можете попробовать что-то вроде:
var differences = list2.Where(l2 => !list1.Any(l1 => l1.Name == l2.Name && l1.Size == l2.Size));
Или, если вы предпочитаете:
var differences = list2.Where(l2 => list1.All(l1 => l1.Name != l2.Name || l1.Size != l2.Size));
Решение, которое я использовал, не может быть описано как быстрое, но это не моя забота, и оно делает то, что я хочу, в том смысле, что оно может быть повторно использовано и не ограничено каким-либо конкретным классом.
Он использует Newtonsoft.Библиотека Json для сериализации объекта в строку и последующего сравнения результата. Это также имеет преимущество работы с анонимными классами и вложенными классами.
Я предполагаю, что способ сравнения заключается в том, что он сначала вызывает GetHashCode на оба объекта и если они совпадают, то вызывает Equals, что в этой процедуре будет означать, что соответствующие объекты будут сериализованы дважды.
public class JSonEqualityComparer<T> : IEqualityComparer<T> { public bool Equals(T x, T y) { return String.Equals ( Newtonsoft.Json.JsonConvert.SerializeObject(x), Newtonsoft.Json.JsonConvert.SerializeObject(y) ); } public int GetHashCode(T obj) { return Newtonsoft.Json.JsonConvert.SerializeObject(obj).GetHashCode(); } } public static partial class LinqExtensions { public static IEnumerable<T> ExceptUsingJSonCompare<T> (this IEnumerable<T> first, IEnumerable<T> second) { return first.Except(second, new JSonEqualityComparer<T>()); } }
Чтобы использовать его вы меняете местами кроме как с ExceptUsingJSonCompare, например:
var differences = list2.ExceptUsingJSonCompare(list1);
Лучший способ, которым я обычно пользуюсь, - это попробовать иметь уникальный / составной ключ в каждом объекте, и таким образом вы только сравниваете ключевую часть и не должны сравнивать другие биты.
Вы можете использовать отражение, которое будет проходить через каждое публичное свойство класса (которое будет очень общим) и сравнивать их, но это будет медленнее.