File: System\Collections\Immutable\ImmutableExtensions.cs
Web Access
Project: src\src\libraries\System.Collections.Immutable\src\System.Collections.Immutable.csproj (System.Collections.Immutable)
// 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 System.Linq;
using System.Reflection;
 
namespace System.Collections.Immutable
{
    /// <summary>
    /// Extension methods for immutable types.
    /// </summary>
    internal static partial class ImmutableExtensions
    {
        internal static bool IsValueType<T>()
        {
#if NET
            return typeof(T).IsValueType;
#else
            if (default(T) != null)
            {
                return true;
            }
 
            Type t = typeof(T);
            if (t.IsConstructedGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return true;
            }
 
            return false;
#endif
        }
 
        /// <summary>
        /// Provides a known wrapper around a sequence of elements that provides the number of elements
        /// and an indexer into its contents.
        /// </summary>
        /// <typeparam name="T">The type of elements in the collection.</typeparam>
        /// <param name="sequence">The collection.</param>
        /// <returns>An ordered collection.  May not be thread-safe.  Never null.</returns>
        internal static IReadOnlyList<T> AsReadOnlyList<T>(this IEnumerable<T> sequence)
        {
            Requires.NotNull(sequence, nameof(sequence));
 
            if (sequence is IReadOnlyList<T> orderedCollection)
            {
                return orderedCollection;
            }
 
            if (sequence is IList<T> listOfT)
            {
                return new ListOfTWrapper<T>(listOfT);
            }
 
            // It would be great if SortedSet<T> and SortedDictionary<T> provided indexers into their collections,
            // but since they don't we have to clone them to an array.
            return new FallbackWrapper<T>(sequence);
        }
 
        /// <summary>
        /// Clears the specified stack.  For empty stacks, it avoids the call to <see cref="Stack{T}.Clear"/>, which
        /// avoids a call into the runtime's implementation of <see cref="Array.Clear(Array, int, int)"/>, helping performance,
        /// in particular around inlining.  <see cref="Stack{T}.Count"/> typically gets inlined by today's JIT, while
        /// <see cref="Stack{T}.Clear"/> and <see cref="Array.Clear(Array, int, int)"/> typically don't.
        /// </summary>
        /// <typeparam name="T">Specifies the type of data in the stack to be cleared.</typeparam>
        /// <param name="stack">The stack to clear.</param>
        internal static void ClearFastWhenEmpty<T>(this Stack<T> stack)
        {
            if (stack.Count > 0)
            {
                stack.Clear();
            }
        }
 
        /// <summary>
        /// Gets a disposable enumerable that can be used as the source for a C# foreach loop
        /// that will not box the enumerator if it is of a particular type.
        /// </summary>
        /// <typeparam name="T">The type of value to be enumerated.</typeparam>
        /// <typeparam name="TEnumerator">The type of the Enumerator struct.</typeparam>
        /// <param name="enumerable">The collection to be enumerated.</param>
        /// <returns>A struct that enumerates the collection.</returns>
        internal static DisposableEnumeratorAdapter<T, TEnumerator> GetEnumerableDisposable<T, TEnumerator>(this IEnumerable<T> enumerable)
            where TEnumerator : struct, IStrongEnumerator<T>, IEnumerator<T>
        {
            Requires.NotNull(enumerable, nameof(enumerable));
 
            if (enumerable is IStrongEnumerable<T, TEnumerator> strongEnumerable)
            {
                return new DisposableEnumeratorAdapter<T, TEnumerator>(strongEnumerable.GetEnumerator());
            }
            else
            {
                // Consider for future: we could add more special cases for common
                // mutable collection types like List<T>+Enumerator and such.
                return new DisposableEnumeratorAdapter<T, TEnumerator>(enumerable.GetEnumerator());
            }
        }
 
        /// <summary>
        /// Wraps a <see cref="IList{T}"/> as an ordered collection.
        /// </summary>
        /// <typeparam name="T">The type of element in the collection.</typeparam>
        private sealed class ListOfTWrapper<T> : IReadOnlyList<T>
        {
            /// <summary>
            /// The list being exposed.
            /// </summary>
            private readonly IList<T> _collection;
 
            /// <summary>
            /// Initializes a new instance of the <see cref="ListOfTWrapper{T}"/> class.
            /// </summary>
            /// <param name="collection">The collection.</param>
            internal ListOfTWrapper(IList<T> collection)
            {
                Requires.NotNull(collection, nameof(collection));
                _collection = collection;
            }
 
            /// <summary>
            /// Gets the count.
            /// </summary>
            public int Count
            {
                get { return _collection.Count; }
            }
 
            /// <summary>
            /// Gets the <typeparamref name="T"/> at the specified index.
            /// </summary>
            public T this[int index]
            {
                get { return _collection[index]; }
            }
 
            /// <summary>
            /// Returns an enumerator that iterates through the collection.
            /// </summary>
            /// <returns>
            /// A <see cref="IEnumerator{T}"/> that can be used to iterate through the collection.
            /// </returns>
            public IEnumerator<T> GetEnumerator()
            {
                return _collection.GetEnumerator();
            }
 
            /// <summary>
            /// Returns an enumerator that iterates through a collection.
            /// </summary>
            /// <returns>
            /// An <see cref="IEnumerator"/> object that can be used to iterate through the collection.
            /// </returns>
            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
        }
 
        /// <summary>
        /// Wraps any <see cref="IEnumerable{T}"/> as an ordered, indexable list.
        /// </summary>
        /// <typeparam name="T">The type of element in the collection.</typeparam>
        private sealed class FallbackWrapper<T> : IReadOnlyList<T>
        {
            /// <summary>
            /// The original sequence.
            /// </summary>
            private readonly IEnumerable<T> _sequence;
 
            /// <summary>
            /// The list-ified sequence.
            /// </summary>
#pragma warning disable CA1859
            private IList<T>? _collection;
#pragma warning restore
 
            /// <summary>
            /// Initializes a new instance of the <see cref="FallbackWrapper{T}"/> class.
            /// </summary>
            /// <param name="sequence">The sequence.</param>
            internal FallbackWrapper(IEnumerable<T> sequence)
            {
                Requires.NotNull(sequence, nameof(sequence));
                _sequence = sequence;
            }
 
            /// <summary>
            /// Gets the count.
            /// </summary>
            public int Count
            {
                get
                {
                    if (_collection == null)
                    {
                        int count;
                        if (_sequence.TryGetCount(out count))
                        {
                            return count;
                        }
 
                        _collection = _sequence.ToArray();
                    }
 
                    return _collection.Count;
                }
            }
 
            /// <summary>
            /// Gets the <typeparamref name="T"/> at the specified index.
            /// </summary>
            public T this[int index]
            {
                get
                {
                    _collection ??= _sequence.ToArray();
 
                    return _collection[index];
                }
            }
 
            /// <summary>
            /// Returns an enumerator that iterates through the collection.
            /// </summary>
            /// <returns>
            /// A <see cref="IEnumerator{T}"/> that can be used to iterate through the collection.
            /// </returns>
            public IEnumerator<T> GetEnumerator()
            {
                return _sequence.GetEnumerator();
            }
 
            /// <summary>
            /// Returns an enumerator that iterates through a collection.
            /// </summary>
            /// <returns>
            /// An <see cref="IEnumerator"/> object that can be used to iterate through the collection.
            /// </returns>
            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
        }
    }
}