C# IEnumerable ๋ฐ yield - slog

C#์˜ IEnumerable๊ณผ LINQ, yield ํ‚ค์›Œ๋“œ๋กœ ๋ฉ”๋ชจ๋ฆฌ ์žฌํ• ๋‹น ์—†๋Š” ๋ชฉ๋ก ์ˆœํšŒํ•˜๊ธฐ

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๋ช… ์ •๋ณด

runtime/src/libraries/System.Linq/src/System/Linq at master ยท dotnet/runtime (github.com)