LINQ DistinctBy 소스 코드 살펴보기

몇 주 전에 원래 이 게시물을 my blog에 게시했습니다. LINQ에 대한 포스트 시리즈의 일부입니다.

"당신은 의존성의 소스 코드를 읽을 준비가 되어 있어야 하고, 의지가 있고, 읽을 수 있어야 합니다."제 Monday Links의 과거 판에서 찾아 공유한 조언입니다.

그 조언에 영감을 받아 LINQ DistinctBy 소스 코드를 살펴보기로 했습니다. 새로운 LINQ DistinctyBy 메서드 내부에 무엇이 있는지 살펴보겠습니다.

LINQ DistinctBy 메서드의 기능은 무엇입니까?



DistinctBy는 해당 속성 중 하나를 기반으로 고유한 값을 포함하는 개체를 반환합니다. 일반 값뿐만 아니라 복잡한 객체의 컬렉션에서도 작동합니다.

DistinctBy는 new LINQ methods introduced in .NET 6 중 하나입니다.

다음 코드 샘플은 출시 연도별로 고유한 영화를 찾는 방법을 보여줍니다.

var movies = new List<Movie>
{
    new Movie("Schindler's List", 1993, 8.9f),
    new Movie("The Lord of the Rings: The Return of the King", 2003, 8.9f),
    new Movie("Pulp Fiction", 1994, 8.8f),
    new Movie("Forrest Gump", 1994, 8.7f),
    new Movie("Inception", 2010, 8.7f)
};

// Here we use the DistinctBy method with the ReleaseYear property
var distinctByReleaseYear = movies.DistinctBy(movie => movie.ReleaseYear);
//                                 ^^^^^^^^^^

foreach (var movie in distinctByReleaseYear)
{
    Console.WriteLine($"{movie.Name}: [{movie.ReleaseYear}]");
}

// Output:
// Schindler's List: [1993]
// The Lord of the Rings: The Return of the King: [2003]
// Pulp Fiction: [1994]
// Inception: [2010]

record Movie(string Name, int ReleaseYear, float Score);


영화 목록에 DistinctBy()를 사용했음을 알 수 있습니다. 우리는 발견된 각 고유한 개봉 연도에 대해 하나의 영화를 찾기 위해 개봉 연도 목록에 사용하지 않았습니다.


우와! 그는 그 파이의 소스 코드를 들여다보고 싶었다. 사진 제공: Bing Han on Unsplash

LINQ DistinctBy 소스 코드



이것은 DistinctBy 메서드의 소스 코드입니다. [Source]

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
    if (source is null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
    }
    if (keySelector is null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
    }

    // Step 1
    return DistinctByIterator(source, keySelector, comparer);
}

private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
    // Step 2
    using IEnumerator<TSource> enumerator = source.GetEnumerator();

    // Step 3
    if (enumerator.MoveNext())
    {
        // Step 4
        var set = new HashSet<TKey>(DefaultInternalSetCapacity, comparer);
        do
        {
            // Step 5
            TSource element = enumerator.Current;
            if (set.Add(keySelector(element)))
            {
                yield return element;
            }
        }
        // Step 6
        while (enumerator.MoveNext());
    }
}


글쎄, 그것은 그렇게 복잡해 보이지 않습니다. 그것을 통해 가자.

1. 입력 컬렉션 반복



먼저 DistinctBy()는 해당 매개변수를 확인하고 DistinctByIterator()를 호출하여 시작합니다. 이는 다른 LINQ 메서드의 일반적인 패턴입니다. 한 메서드에서 매개 변수를 확인한 다음 자식 반복기 메서드를 호출하여 실제 논리를 수행합니다. (위 코드 샘플에서 1단계 참조)

그런 다음 DistinctByIterator()using 선언을 사용하여 입력 컬렉션의 기본 열거자를 초기화합니다. IEnumerable 유형에는 GetEnumerator() 메소드가 있습니다. (2단계 참조)
IEnumerator 유형에는 열거자를 다음 위치로 이동하는 MoveNext() 메서드와 요소를 현재 위치에 유지하는 Current 속성이 있습니다.

모음이 비어 있거나 반복자가 모음 끝에 도달하면 MoveNext()false를 반환합니다. 그리고 MoveNext()true를 반환하면 Current가 해당 위치의 요소로 업데이트됩니다. [Source]

그런 다음 입력 컬렉션 읽기를 시작하기 위해 반복자가 MoveNext()를 호출하는 컬렉션의 초기 위치에 배치됩니다. (3단계를 참조하십시오.) 이 첫 번째if는 컬렉션이 비어 있는 경우 다음 단계에서 세트를 생성하여 메모리 할당을 방지합니다.

2. 고유한 요소 찾기



그런 다음 DistinctByIterator()는 기본 용량과 선택적 비교자를 사용하여 집합을 생성합니다. 이 세트는 이미 발견된 고유 키를 추적합니다. (4단계 참조)

다음 단계는 현재 요소를 읽고 해당 키를 세트에 추가하는 것입니다. (5 참조)

집합에 동일한 요소가 포함되어 있지 않으면 Add()true를 반환하고 집합에 추가합니다. 그렇지 않으면 false 를 반환합니다. 그리고 세트가 용량을 초과하면 세트 크기가 조정됩니다. [Source]

현재 요소의 키가 세트에 추가된 경우 요소는 yield return 키워드와 함께 반환됩니다. 이렇게 하면 DistinctByIterator()가 한 번에 하나의 요소를 반환합니다.

5단계는 do-while 루프 안에 래핑됩니다. 열거자가 컬렉션 끝에 도달할 때까지 실행됩니다. (6단계 참조)

짜잔! 이것이 바로 DistinctBy 소스 코드입니다. 간단하지만 효과적인. 결국 그렇게 위협적이지는 않습니다. 트릭은 세트를 사용하는 것이 었습니다. 규칙과 패턴을 선택하기 위해 표준 라이브러리의 소스 코드를 읽는 것은 좋은 연습입니다.

LINQ 및 기타 방법에 대해 알아보려면 내 블로그에서 myquick guide to LINQ를 확인하십시오. 15분 이내에 LINQ 작업을 시작하기 위해 알아야 할 모든 것.

여기요! 저는 소프트웨어 엔지니어이자 평생 학습자인 Cesar입니다. 내 작업을 지원하려면 교육에 대한 내Getting Started with LINQ course에서 이러한 방법과 기타 LINQ 방법을 자세히 다룹니다.

즐거운 코딩하세요!

좋은 웹페이지 즐겨찾기