LINQ DistinctBy 소스 코드 살펴보기
"당신은 의존성의 소스 코드를 읽을 준비가 되어 있어야 하고, 의지가 있고, 읽을 수 있어야 합니다."제 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 방법을 자세히 다룹니다.
즐거운 코딩하세요!
Reference
이 문제에 관하여(LINQ DistinctBy 소스 코드 살펴보기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/canro91/peeking-into-linq-distinctby-source-code-48a2텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)