File: Microsoft\Internal\Collections\CollectionServices.cs
Web Access
Project: src\src\libraries\System.ComponentModel.Composition\src\System.ComponentModel.Composition.csproj (System.ComponentModel.Composition)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
 
namespace Microsoft.Internal.Collections
{
    internal static partial class CollectionServices
    {
        private static readonly Type StringType = typeof(string);
        private static readonly Type IEnumerableType = typeof(IEnumerable);
        private static readonly Type IEnumerableOfTType = typeof(IEnumerable<>);
        private static readonly Type ICollectionOfTType = typeof(ICollection<>);
 
        public static bool IsEnumerableOfT(Type type)
        {
            if (type.IsGenericType)
            {
                Type genericType = type.GetGenericTypeDefinition().UnderlyingSystemType;
 
                if (genericType == IEnumerableOfTType)
                {
                    return true;
                }
            }
            return false;
        }
 
        public static Type? GetEnumerableElementType(Type type)
        {
            if (type.UnderlyingSystemType == StringType || !IEnumerableType.IsAssignableFrom(type))
            {
                return null;
            }
 
            if (ReflectionServices.TryGetGenericInterfaceType(type, IEnumerableOfTType, out Type? closedType))
            {
                return closedType.GetGenericArguments()[0];
            }
 
            return null;
        }
 
        public static Type? GetCollectionElementType(Type type)
        {
            if (ReflectionServices.TryGetGenericInterfaceType(type, ICollectionOfTType, out Type? closedType))
            {
                return closedType.GetGenericArguments()[0];
            }
 
            return null;
        }
 
        public static ReadOnlyCollection<T> ToReadOnlyCollection<T>(this IEnumerable<T> source)
        {
            ArgumentNullException.ThrowIfNull(source);
 
            return Array.AsReadOnly(source.AsArray());
        }
 
        public static IEnumerable<T>? ConcatAllowingNull<T>(this IEnumerable<T>? source, IEnumerable<T>? second)
        {
            if (second == null || !second.Any())
            {
                return source;
            }
 
            if (source == null || !source.Any())
            {
                return second;
            }
 
            return source.Concat(second);
        }
 
        public static ICollection<T>? ConcatAllowingNull<T>(this ICollection<T>? source, ICollection<T>? second)
        {
            if (second == null || (second.Count == 0))
            {
                return source;
            }
 
            if (source == null || (source.Count == 0))
            {
                return second;
            }
 
            List<T> result = new List<T>(source);
            result.AddRange(second);
 
            return result;
        }
 
        public static List<T>? FastAppendToListAllowNulls<T>(this List<T>? source, IEnumerable<T>? second)
        {
            if (second == null)
            {
                return source;
            }
 
            // if there's nothing in the source, return the second
            if ((source == null) || (source.Count == 0))
            {
                return second.AsList();
            }
 
            // if the second is List<T>, and contains very few elements there's no need for AddRange
            if (second is List<T> secondAsList)
            {
                if (secondAsList.Count == 0)
                {
                    return source;
                }
                else if (secondAsList.Count == 1)
                {
                    source.Add(secondAsList[0]);
                    return source;
                }
            }
 
            // last resort - nothing is null, need to append
            source.AddRange(second);
            return source;
 
        }
 
        private static List<T> FastAppendToListAllowNulls<T>(this List<T>? source, T value)
        {
            source ??= new List<T>();
            source.Add(value);
 
            return source;
        }
 
        public static List<T>? FastAppendToListAllowNulls<T>(
                        this List<T>? source, T? value,
                        IEnumerable<T>? second)
            where T : class
        {
            if (second == null)
            {
                Debug.Assert(value != null);
                source = source.FastAppendToListAllowNulls(value);
            }
            else
            {
                source = source.FastAppendToListAllowNulls(second);
            }
            return source;
        }
 
        public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
        {
            foreach (T t in source)
            {
                action.Invoke(t);
            }
        }
 
        public static EnumerableCardinality GetCardinality<T>(this IEnumerable<T> source)
        {
            ArgumentNullException.ThrowIfNull(source);
 
            // Cast to ICollection instead of ICollection<T> for performance reasons.
            if (source is ICollection collection)
            {
                return collection.Count switch
                {
                    0 => EnumerableCardinality.Zero,
                    1 => EnumerableCardinality.One,
                    _ => EnumerableCardinality.TwoOrMore,
                };
            }
 
            using (var enumerator = source.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                {
                    return EnumerableCardinality.Zero;
                }
 
                if (!enumerator.MoveNext())
                {
                    return EnumerableCardinality.One;
                }
 
                return EnumerableCardinality.TwoOrMore;
            }
        }
 
        public static Stack<T> Copy<T>(this Stack<T> stack)
        {
            ArgumentNullException.ThrowIfNull(stack);
 
            // Stack<T>.GetEnumerator walks from top to bottom
            // of the stack, whereas Stack<T>(IEnumerable<T>)
            // pushes to bottom from top, so we need to reverse
            // the stack to get them in the right order.
            return new Stack<T>(stack.Reverse());
        }
 
        public static T[] AsArray<T>(this IEnumerable<T> enumerable)
        {
            if (enumerable is T[] array)
            {
                return array;
            }
 
            return enumerable.ToArray();
        }
 
        public static List<T> AsList<T>(this IEnumerable<T> enumerable)
        {
            if (enumerable is List<T> list)
            {
                return list;
            }
 
            return enumerable.ToList();
        }
 
        public static bool IsArrayEqual<T>(this T[] thisArray, T[] thatArray)
        {
            if (thisArray.Length != thatArray.Length)
            {
                return false;
            }
 
            for (int i = 0; i < thisArray.Length; i++)
            {
                if (!thisArray[i]!.Equals(thatArray[i]))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        public static bool IsCollectionEqual<T>(this IList<T> thisList, IList<T> thatList)
        {
            if (thisList.Count != thatList.Count)
            {
                return false;
            }
 
            for (int i = 0; i < thisList.Count; i++)
            {
                if (!thisList[i]!.Equals(thatList[i]))
                {
                    return false;
                }
            }
 
            return true;
        }
    }
}