File: Validation\TypeExtensions.cs
Web Access
Project: src\src\Http\Http.Abstractions\src\Microsoft.AspNetCore.Http.Abstractions.csproj (Microsoft.AspNetCore.Http.Abstractions)
// 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;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
 
namespace Microsoft.AspNetCore.Http.Validation;
 
internal static class TypeExtensions
{
    /// <summary>
    /// Determines whether the specified type is an enumerable type.
    /// </summary>
    /// <param name="type">The type to check.</param>
    /// <returns><see langword="true"/> if the type is enumerable; otherwise, <see langword="false"/>.</returns>
    public static bool IsEnumerable(this Type type)
    {
        // Check if type itself is an IEnumerable
        if (type.IsGenericType &&
            (type.GetGenericTypeDefinition() == typeof(IEnumerable<>) ||
            type.GetGenericTypeDefinition() == typeof(ICollection<>) ||
            type.GetGenericTypeDefinition() == typeof(List<>) ||
            type.GetGenericTypeDefinition() == typeof(IList<>)))
        {
            return true;
        }
 
        // Or an array
        if (type.IsArray)
        {
            return true;
        }
 
        // Then evaluate if it implements IEnumerable and is not a string
        if (typeof(IEnumerable).IsAssignableFrom(type) &&
            type != typeof(string))
        {
            return true;
        }
 
        return false;
    }
 
    /// <summary>
    /// Determines whether the specified type is a nullable type.
    /// </summary>
    /// <param name="type">The type to check.</param>
    /// <returns><see langword="true"/> if the type is nullable; otherwise, <see langword="false"/>.</returns>
    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType)
        {
            return false;
        }
 
        if (type.IsGenericType &&
            type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            return true;
        }
 
        return false;
    }
 
    /// <summary>
    /// Tries to get the <see cref="RequiredAttribute"/> from the specified array of validation attributes.
    /// </summary>
    /// <param name="attributes">The array of <see cref="ValidationAttribute"/> to search.</param>
    /// <param name="requiredAttribute">The found <see cref="RequiredAttribute"/> if available, otherwise null.</param>
    /// <returns><see langword="true"/> if a <see cref="RequiredAttribute"/> is found; otherwise, <see langword="false"/>.</returns>
    public static bool TryGetRequiredAttribute(this ValidationAttribute[] attributes, [NotNullWhen(true)] out RequiredAttribute? requiredAttribute)
    {
        foreach (var attribute in attributes)
        {
            if (attribute is RequiredAttribute requiredAttr)
            {
                requiredAttribute = requiredAttr;
                return true;
            }
        }
 
        requiredAttribute = null;
        return false;
    }
 
    /// <summary>
    /// Gets all types that the specified type implements or inherits from.
    /// </summary>
    /// <param name="type">The type to analyze.</param>
    /// <returns>A collection containing all implemented interfaces and all base types of the given type.</returns>
    public static List<Type> GetAllImplementedTypes([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type)
    {
        ArgumentNullException.ThrowIfNull(type);
 
        var implementedTypes = new List<Type>();
 
        // Yield all interfaces directly and indirectly implemented by this type
        foreach (var interfaceType in type.GetInterfaces())
        {
            implementedTypes.Add(interfaceType);
        }
 
        // Finally, walk up the inheritance chain
        var baseType = type.BaseType;
        while (baseType != null && baseType != typeof(object))
        {
            implementedTypes.Add(baseType);
            baseType = baseType.BaseType;
        }
 
        return implementedTypes;
    }
 
    /// <summary>
    /// Determines whether the specified type implements the given interface.
    /// </summary>
    /// <param name="type">The type to check.</param>
    /// <param name="interfaceType">The interface type to check for.</param>
    /// <returns>True if the type implements the specified interface; otherwise, false.</returns>
    public static bool ImplementsInterface(this Type type, Type interfaceType)
    {
        ArgumentNullException.ThrowIfNull(type);
        ArgumentNullException.ThrowIfNull(interfaceType);
 
        // Check if interfaceType is actually an interface
        if (!interfaceType.IsInterface)
        {
            throw new ArgumentException($"Type {interfaceType.FullName} is not an interface.", nameof(interfaceType));
        }
 
        return interfaceType.IsAssignableFrom(type);
    }
}