[C#] 익명 유형을 가진 LINQ Select Distinct

그래서 객체 모음이 있습니다. 정확한 유형은 중요하지 않습니다. 그것으로부터 특정 속성 쌍의 모든 고유 쌍을 추출하고 싶습니다.

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

그래서 내 질문은 :이 경우 기본 객체 같음 (각 객체가 새 것이기 때문에 나에게 쓸모가 없음)을 사용하거나 다른 같음 (이 경우 알파와 브라보의 동일한 값)을 수행하도록 지시받을 수 있습니다 => 동일한 인스턴스)? 그렇지 않은 경우 해당 결과를 달성 할 수있는 방법이 있습니까?



답변

K. Scott Allen의 훌륭한 게시물을 읽으십시오.

그리고 모두를위한 평등 … 익명 유형

짧은 대답 (및 인용) :

익명 유형에 대해 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();
    }
}

그런 다음 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();


답변

경우 AlphaBravo일반 클래스에서 모두 상속, 당신은 구현하여 부모 클래스에서 동등 검사를 지시 할 수있을 것입니다 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 인터페이스를 구현하거나 단순히 (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 “Breuse I am”) GetHashCode에서 Person 개체가 아닌 Name 속성의 해시를 반환했습니다.

그것을 시도하면 ISA가 작동합니다. 감사합니다, Modather Sadik