TypeExtensions.cs 4.08 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
using System;
using System.Collections;
using System.Collections.Generic;

namespace Syroot.BinaryData
{
    /// <summary>
    /// Represents a collection of extension methods for the <see cref="Type"/> class.
    /// </summary>
    internal static class TypeExtensions
    {
        // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
        
        /// <summary>
        /// Returns a value indicating whether the given <paramref name="type"/> is enumerable. Returns <c>false</c> for
        /// non-enumerable objects and strings.
        /// </summary>
        /// <param name="type">The type which should be checked.</param>
        /// <returns><c>true</c> if the type is enumerable and not a string; otherwise <c>false</c>.</returns>
        internal static bool IsEnumerable(this Type type)
        {
            return type != typeof(String) && (type.IsArray || typeof(IEnumerable).IsAssignableFrom(type));
        }

        /// <summary>
        /// Gets the element type of <see cref="IEnumerable"/> instances. Returns <c>null</c> for non-enumerable objects
        /// and strings.
        /// </summary>
        /// <param name="type">The type which element type should be returned.</param>
        /// <returns>The type of the elements, or <c>null</c>.</returns>
        internal static Type GetEnumerableElementType(this Type type)
        {
            // Do not handle strings as enumerables, they are stored differently.
            if (type == typeof(String))
            {
                return null;
            }

            // Check for array instances.
            if (type.IsArray)
            {
                Type elementType;
                if (type.GetArrayRank() > 1 || (elementType = type.GetElementType()).IsArray)
                {
                    throw new NotImplementedException(
                        $"Type {type} is a multidimensional array and not supported at the moment.");
                }
                return elementType;
            }

            // Check for IEnumerable instances. Only the first implementation of IEnumerable<> is returned.
            if (typeof(IEnumerable).IsAssignableFrom(type))
            {
                foreach (Type interfaceType in type.GetInterfaces())
                {
                    if (interfaceType.IsGenericType
                        && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                    {
                        return interfaceType.GetGenericArguments()[0];
                    }
                }
            }

            return null;
        }

        internal static bool TryGetEnumerableElementType(this Type type, out Type elementType)
        {
            // Do not handle strings as enumerables, they are stored differently.
            if (type != typeof(String))
            {
                // Check for array instances.
                if (type.IsArray)
                {
                    Type elemType;
                    if (type.GetArrayRank() > 1 || (elemType = type.GetElementType()).IsArray)
                    {
                        throw new NotImplementedException(
                            $"Type {type} is a multidimensional array and not supported at the moment.");
                    }
                    elementType = elemType;
                    return true;
                }

                // Check for IEnumerable instances. Only the first implementation of IEnumerable<> is returned.
                if (typeof(IEnumerable).IsAssignableFrom(type))
                {
                    foreach (Type interfaceType in type.GetInterfaces())
                    {
                        if (interfaceType.IsGenericType
                            && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                        {
                            elementType = interfaceType.GetGenericArguments()[0];
                            return true;
                        }
                    }
                }
            }

            elementType = null;
            return false;
        }
    }
}