LINQ Select Distinct с анонимными типами
Так у меня есть коллекция объектов. Точный тип не имеет значения. Из него я хочу извлечь все уникальные пары пары конкретных свойств, таким образом:
myObjectCollection.Select(item=>new
{
Alpha = item.propOne,
Bravo = item.propTwo
}
).Distinct();
Итак, мой вопрос: Будет ли Distinct в этом случае использовать объект по умолчанию equals (который будет бесполезен для меня, поскольку каждый объект является новым) или можно сказать, что он делает разные equals (в этом случае равные значения Alpha и Bravo => равные экземпляры)? Есть ли способ достичь этого результата, если это не делает этого?
8 ответов:
прочтите отличный пост К. Скотта Аллена здесь:
и равенство для всех ... Анонимные Типы
короткий ответ (и я цитирую):
оказывается, компилятор C# переопределяет Equals и GetHashCode для анонимного типы. Реализация двух переопределенные методы используют все общедоступные свойства типа для вычисления хэш-код объекта и тест для равенство. Если два объекта одного и того же анонимный типа у всех одинаковые значения их свойств – объекты равны.
таким образом, совершенно безопасно использовать метод Distinct() в запросе, который возвращает анонимные типы.
public class DelegateComparer<T> : IEqualityComparer<T> { private Func<T, T, bool> _equals; private Func<T, int> _hashCode; public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode) { _equals= equals; _hashCode = hashCode; } public bool Equals(T x, T y) { return _equals(x, y); } public int GetHashCode(T obj) { if(_hashCode!=null) return _hashCode(obj); return obj.GetHashCode(); } } public static class Extensions { public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, Func<T, T, bool> equals, Func<T,int> hashCode) { return items.Distinct(new DelegateComparer<T>(equals, hashCode)); } public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, Func<T, T, bool> equals) { return items.Distinct(new DelegateComparer<T>(equals,null)); } } var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName}) .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();извините за испорченное форматирование ранее
интересно, что он работает в C# , но не в VB
возвращает 26 букв:
var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"; MyBet.ToCharArray() .Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()}) .Distinct() .Dump();возвращает 52...
Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" MyBet.ToCharArray() _ .Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _ .Distinct() _ .Dump()
Я провел небольшой тест и выяснили, что если свойства являются типами значений, кажется, работает нормально. Если они не являются типами значений, то тип должен предоставить собственные реализации Equals и GetHashCode для его работы. Струны, я думаю, сработают.
вы можете создать свой собственный метод расширения, который принимает лямбда-выражение. Вот пример
создайте класс, производный от интерфейса IEqualityComparer
public class DelegateComparer<T> : IEqualityComparer<T> { private Func<T, T, bool> _equals; private Func<T, int> _hashCode; public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode) { _equals= equals; _hashCode = hashCode; } public bool Equals(T x, T y) { return _equals(x, y); } public int GetHashCode(T obj) { if(_hashCode!=null) return _hashCode(obj); return obj.GetHashCode(); } }затем создайте свой собственный метод расширения
public static class Extensions { public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, Func<T, T, bool> equals, Func<T,int> hashCode) { return items.Distinct(new DelegateComparer<T>(equals, hashCode)); } public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, Func<T, T, bool> equals) { return items.Distinct(new DelegateComparer<T>(equals,null)); } }и вы можете использовать этот метод найти различные элементы
var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName}) .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();
если
AlphaиBravoоба наследуют от общего класса, вы сможете диктовать проверку равенства в родительском классе, реализуяIEquatable<T>.например:
public class CommonClass : IEquatable<CommonClass> { // needed for Distinct() public override int GetHashCode() { return base.GetHashCode(); } public bool Equals(CommonClass other) { if (other == null) return false; return [equality test]; } }
Эй там я получил ту же проблему, и я нашел решение. Вы должны реализовать IEquatable интерфейс или просто переопределить (Равно & GetHashCode) Методы. Но это не трюк, трюк, приходящий в методе GetHashCode. Вы не должны возвращать хэш-код объекта вашего класса, но вы должны вернуть хэш-объекта, которые вы хотите сравнить, как это.
public override bool Equals(object obj) { Person p = obj as Person; if ( obj == null ) return false; if ( object.ReferenceEquals( p , this ) ) return true; if ( p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian ) return true; return false; //return base.Equals( obj ); } public override int GetHashCode() { return Name.GetHashCode(); }Как вы видите, я получил класс под названием person получил 3 свойства (имя, возраст, IsEgyptian " потому что я am") в GetHashCode я вернул хэш свойства Name, а не объекта Person.
попробуйте, и он будет работать ISA. Спасибо, Модатер Садик
для того, чтобы он работал в VB.NET, вам нужно указать
Keyключевое слово перед каждым свойством в анонимном типе, так же, как это:myObjectCollection.Select(Function(item) New With { Key .Alpha = item.propOne, Key .Bravo = item.propTwo }).Distinct()я боролся с этим, я думал VB.NET не поддерживает этот тип функции, но на самом деле это так.
Comments