Enumerable의 LINQ스타일 확장메소드를 보면 목록을 조회하거나 정렬할 때 목록을 반환하기 위해 메모리를 재할당 하지 않습니다. 비밀은 yield 키워드인데요, yield 키워드를 사용하면, 목록을 소비하는 코드와 반환하는 코드를 분리할 수 있는데요, 코드상으로는 분리되어 있지만, 실제 코드 흐름은 두가지 포인트가 결합됩니다.
A.B.C.ToList() 형태로 yield로 반환하는 IEnumerable 목록을 반환하는 메소드를 결합하면, 메모리 재할당 없이 순회목록이 yield에 의해 우아하게 결합합니다.
IEnumerable과 yield, 그리고 결합과 await foreach를 적절히 잘 이용하면, 상태의 전이
를 코드로 우아하게 짤 수 있습니다.
이런 특징들이 함수형 언어의 영향이라고 개인적으로 생각하고 있는데, 명확하게 이거다 라고 할 정도로 정리는 못했습니다. 시간 날 때 코드로 표현하겠습니다.
IEnumerable의 Concat 메소드.
목록의 결합은 실제로 yield return의 연속입니다.
public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) {
if (first == null) throw Error.ArgumentNull("first");
if (second == null) throw Error.ArgumentNull("second");
return ConcatIterator<TSource>(first, second);
}
static IEnumerable<TSource> ConcatIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second) {
foreach (TSource element in first) yield return element;
foreach (TSource element in second) yield return element;
}
delegate를 이용하면 selector
를 구현할 수 있습니다. Enumerable의 LINQ 메소드를 보면, selector
를 Func<TSource, TResult>
형태로 사용했음을 알 수 있습니다. 예를들어,
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) {
if (source == null) throw Error.ArgumentNull("source");
if (selector == null) throw Error.ArgumentNull("selector");
if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Select(selector);
if (source is TSource[]) return new WhereSelectArrayIterator<TSource, TResult>((TSource[])source, null, selector);
if (source is List<TSource>) return new WhereSelectListIterator<TSource, TResult>((List<TSource>)source, null, selector);
return new WhereSelectEnumerableIterator<TSource, TResult>(source, null, selector);
}
의 경우, selector
를 제네릭과 같이 사용해서 Select
메소드와 selector
의 종속성을 제거합니다.
목록을 반환 할 때 빈 목록을 new List<TItem>()
형태로 곧잘 반환합니다. 하지만 이러면 메소드가 호출될 때마다 인스턴스가 생성되므로, Enumerable.Empty<TItem>()
가 좋은 선택일 수 있습니다. 내부적으로는 해당 타입을 정적으로 할당하여 재사용 합니다.
internal class EmptyEnumerable<TElement>
{
public static readonly TElement[] Instance = new TElement[0];
}
LINQ에 익숙해지면 selector
에 의해 자료를 묶었다 풀었다 할 수 있습니다.
GroupBy
: selector 기준으로 묶음
SelectMany
: selector 기준으로 품
예)
list = list.GroupBy(x => x.성별).SelectMany(x => x.OrderBy(y => y.등수).Take(2));
// 결과 : 남자중 성적이 가장 좋은 2명 정보, 여자 중 성적이 가장 좋은 2명 정보
좋은 설명 감사합니다