File: System\Linq\Select.cs
Web Access
Project: src\src\libraries\System.Linq\src\System.Linq.csproj (System.Linq)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using static System.Linq.Utilities;
 
namespace System.Linq
{
    public static partial class Enumerable
    {
        public static IEnumerable<TResult> Select<TSource, TResult>(
            this IEnumerable<TSource> source, Func<TSource, TResult> selector)
        {
            if (source is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
            }
 
            if (selector is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.selector);
            }
 
            if (source is Iterator<TSource> iterator)
            {
                return iterator.Select(selector);
            }
 
            if (source is IList<TSource> ilist)
            {
                if (source is TSource[] array)
                {
                    if (array.Length == 0)
                    {
                        return [];
                    }
 
                    return new ArraySelectIterator<TSource, TResult>(array, selector);
                }
 
                if (source is List<TSource> list)
                {
                    return new ListSelectIterator<TSource, TResult>(list, selector);
                }
 
                return new IListSelectIterator<TSource, TResult>(ilist, selector);
            }
 
            return new IEnumerableSelectIterator<TSource, TResult>(source, selector);
        }
 
        public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
        {
            if (source is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
            }
 
            if (selector is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.selector);
            }
 
            if (IsEmptyArray(source))
            {
                return [];
            }
 
            return SelectIterator(source, selector);
        }
 
        private static IEnumerable<TResult> SelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
        {
            int index = -1;
            foreach (TSource element in source)
            {
                checked
                {
                    index++;
                }
 
                yield return selector(element, index);
            }
        }
 
        /// <summary>
        /// An iterator that maps each item of an <see cref="IEnumerable{TSource}"/>.
        /// </summary>
        /// <typeparam name="TSource">The type of the source enumerable.</typeparam>
        /// <typeparam name="TResult">The type of the mapped items.</typeparam>
        private sealed partial class IEnumerableSelectIterator<TSource, TResult> : Iterator<TResult>
        {
            private readonly IEnumerable<TSource> _source;
            private readonly Func<TSource, TResult> _selector;
            private IEnumerator<TSource>? _enumerator;
 
            public IEnumerableSelectIterator(IEnumerable<TSource> source, Func<TSource, TResult> selector)
            {
                Debug.Assert(source is not null);
                Debug.Assert(selector is not null);
                _source = source;
                _selector = selector;
            }
 
            private protected override Iterator<TResult> Clone() =>
                new IEnumerableSelectIterator<TSource, TResult>(_source, _selector);
 
            public override void Dispose()
            {
                if (_enumerator is not null)
                {
                    _enumerator.Dispose();
                    _enumerator = null;
                }
 
                base.Dispose();
            }
 
            public override bool MoveNext()
            {
                switch (_state)
                {
                    case 1:
                        _enumerator = _source.GetEnumerator();
                        _state = 2;
                        goto case 2;
                    case 2:
                        Debug.Assert(_enumerator is not null);
                        if (_enumerator.MoveNext())
                        {
                            _current = _selector(_enumerator.Current);
                            return true;
                        }
 
                        Dispose();
                        break;
                }
 
                return false;
            }
 
            public override IEnumerable<TResult2> Select<TResult2>(Func<TResult, TResult2> selector) =>
                new IEnumerableSelectIterator<TSource, TResult2>(_source, CombineSelectors(_selector, selector));
        }
 
        /// <summary>
        /// An iterator that maps each item of an array.
        /// </summary>
        /// <typeparam name="TSource">The type of the source array.</typeparam>
        /// <typeparam name="TResult">The type of the mapped items.</typeparam>
        [DebuggerDisplay("Count = {CountForDebugger}")]
        private sealed partial class ArraySelectIterator<TSource, TResult> : Iterator<TResult>
        {
            private readonly TSource[] _source;
            private readonly Func<TSource, TResult> _selector;
 
            public ArraySelectIterator(TSource[] source, Func<TSource, TResult> selector)
            {
                Debug.Assert(source is not null);
                Debug.Assert(selector is not null);
                Debug.Assert(source.Length > 0); // Caller should check this beforehand and return a cached result
                _source = source;
                _selector = selector;
            }
 
            private int CountForDebugger => _source.Length;
 
            private protected override Iterator<TResult> Clone() => new ArraySelectIterator<TSource, TResult>(_source, _selector);
 
            public override bool MoveNext()
            {
                TSource[] source = _source;
                int index = _state - 1;
                if ((uint)index < (uint)source.Length)
                {
                    _state++;
                    _current = _selector(source[index]);
                    return true;
                }
 
                Dispose();
                return false;
            }
 
            public override IEnumerable<TResult2> Select<TResult2>(Func<TResult, TResult2> selector) =>
                new ArraySelectIterator<TSource, TResult2>(_source, CombineSelectors(_selector, selector));
        }
 
        /// <summary>
        /// An iterator that maps each item of a <see cref="List{TSource}"/>.
        /// </summary>
        /// <typeparam name="TSource">The type of the source list.</typeparam>
        /// <typeparam name="TResult">The type of the mapped items.</typeparam>
        [DebuggerDisplay("Count = {CountForDebugger}")]
        private sealed partial class ListSelectIterator<TSource, TResult> : Iterator<TResult>
        {
            private readonly List<TSource> _source;
            private readonly Func<TSource, TResult> _selector;
            private List<TSource>.Enumerator _enumerator;
 
            public ListSelectIterator(List<TSource> source, Func<TSource, TResult> selector)
            {
                Debug.Assert(source is not null);
                Debug.Assert(selector is not null);
                _source = source;
                _selector = selector;
            }
 
            private int CountForDebugger => _source.Count;
 
            private protected override Iterator<TResult> Clone() => new ListSelectIterator<TSource, TResult>(_source, _selector);
 
            public override bool MoveNext()
            {
                switch (_state)
                {
                    case 1:
                        _enumerator = _source.GetEnumerator();
                        _state = 2;
                        goto case 2;
                    case 2:
                        if (_enumerator.MoveNext())
                        {
                            _current = _selector(_enumerator.Current);
                            return true;
                        }
 
                        Dispose();
                        break;
                }
 
                return false;
            }
 
            public override IEnumerable<TResult2> Select<TResult2>(Func<TResult, TResult2> selector) =>
                new ListSelectIterator<TSource, TResult2>(_source, CombineSelectors(_selector, selector));
        }
 
        /// <summary>
        /// An iterator that maps each item of an <see cref="IList{TSource}"/>.
        /// </summary>
        /// <typeparam name="TSource">The type of the source list.</typeparam>
        /// <typeparam name="TResult">The type of the mapped items.</typeparam>
        [DebuggerDisplay("Count = {CountForDebugger}")]
        private sealed partial class IListSelectIterator<TSource, TResult> : Iterator<TResult>
        {
            private readonly IList<TSource> _source;
            private readonly Func<TSource, TResult> _selector;
            private IEnumerator<TSource>? _enumerator;
 
            public IListSelectIterator(IList<TSource> source, Func<TSource, TResult> selector)
            {
                Debug.Assert(source is not null);
                Debug.Assert(selector is not null);
                _source = source;
                _selector = selector;
            }
 
            private int CountForDebugger => _source.Count;
 
            private protected override Iterator<TResult> Clone() => new IListSelectIterator<TSource, TResult>(_source, _selector);
 
            public override bool MoveNext()
            {
                switch (_state)
                {
                    case 1:
                        _enumerator = _source.GetEnumerator();
                        _state = 2;
                        goto case 2;
                    case 2:
                        Debug.Assert(_enumerator is not null);
                        if (_enumerator.MoveNext())
                        {
                            _current = _selector(_enumerator.Current);
                            return true;
                        }
 
                        Dispose();
                        break;
                }
 
                return false;
            }
 
            public override void Dispose()
            {
                if (_enumerator is not null)
                {
                    _enumerator.Dispose();
                    _enumerator = null;
                }
 
                base.Dispose();
            }
 
            public override IEnumerable<TResult2> Select<TResult2>(Func<TResult, TResult2> selector) =>
                new IListSelectIterator<TSource, TResult2>(_source, CombineSelectors(_selector, selector));
        }
    }
}