LINQ Select Distinct с анонимными типами - PullRequest
141 голосов
/ 13 февраля 2009

Итак, у меня есть коллекция предметов. Точный тип не важен. Из него я хочу извлечь все уникальные пары пары определенных свойств, таким образом:

myObjectCollection.Select(item=>new
                                {
                                     Alpha = item.propOne,
                                     Bravo = item.propTwo
                                }
                 ).Distinct();

Таким образом, мой вопрос: будет ли Distinct в этом случае использовать объект по умолчанию, равный (который будет бесполезен для меня, поскольку каждый объект является новым), или можно сказать, что он будет делать разные равные (в этом случае равные значения Альфа и браво => равные экземпляры)? Есть ли способ достичь этого результата, если он этого не делает?

Ответы [ 8 ]

176 голосов
/ 13 февраля 2009

Прочитайте отличный пост К. Скотта Аллена здесь:

И равенство для всех ... Анонимные типы

Краткий ответ (и я цитирую):

Получает переопределения компилятора C # Equals и GetHashCode для анонимных типы. Реализация двух переопределенные методы использует все публичные свойства типа для вычисления хеш-код объекта и тест для равенство. Если два объекта одинаковы анонимный тип есть у всех одинаковый значения их свойств - объекты равны.

Так что совершенно безопасно использовать метод Distinct () для запроса, который возвращает анонимные типы.

14 голосов
/ 27 марта 2009
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();

Извините за испорченное форматирование ранее

5 голосов
/ 26 октября 2009

Интересно, что это работает в 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()
3 голосов
/ 13 февраля 2009

Я провел небольшой тест и обнаружил, что если свойства являются типами значений, похоже, что они работают нормально. Если они не являются типами значений, то для работы этого типа необходимо предоставить собственные реализации Equals и GetHashCode. Я думаю, что строки будут работать.

2 голосов
/ 27 марта 2009

Вы можете создать свой собственный метод Distinct Extension, который принимает лямбда-выражения. Вот пример

Создание класса, производного от интерфейса 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();
    }       
}

Затем создайте свой метод Distinct Extension

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();
0 голосов
/ 13 июля 2018

Для того, чтобы он работал в VB.NET, вам необходимо указать ключевое слово Key перед каждым свойством в анонимном типе, например:

myObjectCollection.Select(Function(item) New With
{
    Key .Alpha = item.propOne,
    Key .Bravo = item.propTwo
}).Distinct()

Я боролся с этим, я думал, что VB.NET не поддерживает этот тип функций, но на самом деле это так.

0 голосов
/ 16 ноября 2011

Привет, у меня та же проблема, и я нашел решение. Вы должны реализовать интерфейс 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 свойства (Name, Age, IsEgyptian "Потому что я есть"). В GetHashCode я вернул хэш свойства Name, а не объекта Person.

Попробуйте, и это будет работать ISA. Спасибо, Модер Садик

0 голосов
/ 13 февраля 2009

Если 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];
    }
}
...