문제

그래서 나는 SO와 다른 곳에서 이에 대한 약 20개의 예제를 살펴봤지만 내가 하려는 작업을 다루는 예제를 찾지 못했습니다.이것 - 명시적 유형 비교기를 인라인으로 지정할 수 있나요? - 필요한 것 같지만 충분하지 않습니다(또는 더 나아가는 방법을 이해하지 못합니다).

  • LoadData 목록이 있고 LoadData 개체에는 참조 유형과 값 유형의 필드가 모두 있습니다.
  • 참조 필드와 값 필드를 혼합하여 그룹화하고 출력을 익명 유형으로 프로젝션해야 합니다.
  • GroupBy 필드를 비교하는 방법을 지정하려면 사용자 지정 IEqualityComparer를 제공해야 하지만 익명 유형입니다.

    private class LoadData
    {
        public PeriodEndDto PeriodEnd { get; set; }
        public ComponentDto Component { get; set; }
        public string GroupCode { get; set; }
        public string PortfolioCode { get; set; }
    }
    

지금까지 내가 본 최고의 GroupBy 쿼리는 다음과 같습니다.

var distinctLoads = list.GroupBy(
    dl => new { PeriodEnd = dl.PeriodEnd, 
                Component = dl.Component, 
                GroupCode = dl.GroupCode },
    (key, data) => new {PeriodEnd = key.PeriodEnd, 
                Component = key.Component, 
                GroupCode = key.GroupCode, 
                PortfolioList = data.Select(d=>d.PortfolioCode)
                                    .Aggregate((g1, g2) => g1 + "," + g2)},
    null);

이 그룹이지만 여전히 중복된 그룹이 있습니다.

  1. GroupBy 필드를 비교하기 위해 사용자 정의 코드를 지정하려면 어떻게 해야 합니까?예를 들어 구성 요소는 Component.Code로 비교할 수 있습니다.
도움이 되었습니까?

해결책

여기서 문제는 키 유형이 익명이라는 것입니다. 즉, 다음을 구현하는 클래스를 선언할 수 없습니다. IEqualityComparer<T> 해당 키 유형에 대해.그러는 동안 가능한 사용자 지정 방식(일반 메서드, 대리자 및 형식 추론을 통해)으로 익명 형식의 동등성을 비교하는 비교기를 작성하는 것은 별로 즐겁지 않습니다.

가장 간단한 두 가지 옵션은 아마도 다음과 같습니다.

  • Equals/GetHashCode를 재정의하여 익명 유형을 "작동"하게 만듭니다. PeriodEndDto 그리고 ComponentDto.어디에서나 사용하고 싶은 자연스러운 평등이 있다면 이것이 아마도 가장 건전한 선택일 것입니다.구현하는 것이 좋습니다 IEquatable<T> 또한
  • 그룹화에 익명 유형을 사용하지 마십시오. 명명된 유형을 사용하고 재정의할 수 있습니다. GetHashCode 그리고 Equals 또는 일반적인 방법으로 사용자 정의 동등 비교자를 작성할 수 있습니다.

편집하다: ProjectionEqualityComparer 정말 작동하지 않을 것입니다.그래도 비슷한 것을 작성하는 것이 가능할 것입니다. CompositeEqualityComparer 이를 통해 여러 "프로젝션 + 비교자" 쌍에서 동등 비교자를 만들 수 있습니다.하지만 익명 유형에 비해 꽤 추악합니다.

다른 팁

편집하다:

Jon Skeet이 지적했듯이 GetHashCode 구현을 잊어버렸기 때문에 이 솔루션에 대해 너무 열심히 생각하지 않는다면 이 솔루션이 실제보다 더 좋아 보입니다.Jon이 그의 대답에서 "매우 유쾌하지 않다"고 말한 것처럼 gethashcode를 구현 해야하는 것은 이러한 접근 방식을 만듭니다. 아마도 이것은 또한 (소위 "설명 할 수없는") 부재에 대한 설명이기도합니다. EqualityComparer<T>.Create() 프레임워크에서.하지 말아야 할 일의 예도 유익할 수 있으므로 참고용으로 답을 남겨 두겠습니다.

원래 답변:

에서 제안한 접근 방식을 사용할 수 있습니다. Comparer<T>.Create .NET 4.5에 도입된 패턴(그러나 .NET 4.5에는 설명할 수 없을 정도로 없음) EqualityComparer<T>).그렇게 하려면 DelegateEqualityComparer<T> 수업:

class DelegateEqualityComparer<T> : EqualityComparer<T>
{
    private readonly Func<T, T, bool> _equalityComparison;

    private DelegateEqualityComparer(Func<T, T, bool> equalityComparison)
    {
        if (equalityComparison == null)
            throw new ArgumentNullException("equalityComparison");
        _equalityComparison = equalityComparison;
    }

    public override bool Equals(T x, T y)
    {
        return _equalityComparison(x, y);
    }

    public static DelegateEqualityComparer<T> Create(
        Func<T, T, bool> equalityComparison)
    {
        return new DelegateEqualityComparer<T>(equalityComparison);
    }
}

그런 다음 GroupBy 메서드 주위에 래퍼를 작성하여 Func<TKey, TKey, bool> 대신하여 대리인 IEqualityComparer<TKey> 매개변수.이러한 메서드는 대리자를 다음과 같이 래핑합니다. DelegateEqualityComparer<T> 인스턴스를 생성하고 이를 해당 GroupBy 메서드에 전달합니다.예:

public static class EnumerableExt
{
    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector,
        Func<TKey, TKey, bool> equalityComparison)
    {
        return source.GroupBy(
            keySelector,
            DelegateEqualityComparer<TKey>.Create(equalityComparison);
    }
}

마지막으로 호출 사이트에서 다음과 같은 표현식을 사용합니다. equalityComparison 논쟁:

(a, b) => a.PeriodEnd.Equals(b.PeriodEnd)
    && a.Component.Code.Equals(b.Component.Code)
    && a.GroupCode.Equals(b.GroupCode)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top