LINQ Выберите "Определить" с анонимными типами

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

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

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

Ответ 1

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

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

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

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

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

Ответ 2

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();

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

Ответ 3

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

Ответ 4

Интересно, что он работает в С#, но не в 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()

Ответ 5

Вы можете создать свой собственный метод 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();

Ответ 6

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

Ответ 7

Привет, у меня такая же проблема, и я нашел решение. Вы должны реализовать интерфейс IEquatable или просто переопределить (Equals и 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. Спасибо, Модед Садик