File: src\libraries\System.Private.CoreLib\src\System\Type.Helpers.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
 
namespace System
{
    // This file collects the longer methods of Type to make the main Type class more readable.
    public abstract partial class Type : MemberInfo, IReflect
    {
        [Obsolete(Obsoletions.LegacyFormatterMessage, DiagnosticId = Obsoletions.LegacyFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public virtual bool IsSerializable
        {
            get
            {
                if ((GetAttributeFlagsImpl() & TypeAttributes.Serializable) != 0)
                    return true;
 
                Type? underlyingType = UnderlyingSystemType;
                if (underlyingType is RuntimeType)
                {
                    do
                    {
                        // In all sane cases we only need to compare the direct level base type with
                        // System.Enum and System.MulticastDelegate. However, a generic parameter can
                        // have a base type constraint that is Delegate or even a real delegate type.
                        // Let's maintain compatibility and return true for them.
                        if (underlyingType == typeof(Delegate) || underlyingType == typeof(Enum))
                            return true;
 
                        underlyingType = underlyingType.BaseType;
                    }
                    while (underlyingType != null);
                }
 
                return false;
            }
        }
 
        public virtual bool ContainsGenericParameters
        {
            get
            {
                if (HasElementType)
                    return GetRootElementType().ContainsGenericParameters;
 
                if (IsGenericParameter)
                    return true;
 
                if (!IsGenericType)
                    return false;
 
                Type[] genericArguments = GetGenericArguments();
                for (int i = 0; i < genericArguments.Length; i++)
                {
                    if (genericArguments[i].ContainsGenericParameters)
                        return true;
                }
 
                return false;
            }
        }
 
        internal Type GetRootElementType()
        {
            Type rootElementType = this;
 
            while (rootElementType.HasElementType)
                rootElementType = rootElementType.GetElementType()!;
 
            return rootElementType;
        }
 
        public bool IsVisible
        {
            get
            {
#if CORECLR
                if (this is RuntimeType rt)
                    return RuntimeTypeHandle.IsVisible(rt);
#endif //CORECLR
 
                if (IsGenericParameter)
                    return true;
 
                if (HasElementType)
                    return GetElementType()!.IsVisible;
 
                Type type = this;
                while (type.IsNested)
                {
                    if (!type.IsNestedPublic)
                        return false;
 
                    // this should be null for non-nested types.
                    type = type.DeclaringType!;
                }
 
                // Now "type" should be a top level type
                if (!type.IsPublic)
                    return false;
 
                if (IsGenericType && !IsGenericTypeDefinition)
                {
                    foreach (Type t in GetGenericArguments())
                    {
                        if (!t.IsVisible)
                            return false;
                    }
                }
 
                return true;
            }
        }
 
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
        public virtual Type[] FindInterfaces(TypeFilter filter, object? filterCriteria)
        {
            ArgumentNullException.ThrowIfNull(filter);
 
            Type?[] c = GetInterfaces();
            int cnt = 0;
            for (int i = 0; i < c.Length; i++)
            {
                if (!filter(c[i]!, filterCriteria))
                    c[i] = null;
                else
                    cnt++;
            }
            if (cnt == c.Length)
                return c!;
 
            Type[] ret = new Type[cnt];
            cnt = 0;
            for (int i = 0; i < c.Length; i++)
            {
                if (c[i] is Type t)
                    ret[cnt++] = t!;
            }
            return ret;
        }
 
        [DynamicallyAccessedMembers(GetAllMembers)]
        public virtual MemberInfo[] FindMembers(MemberTypes memberType, BindingFlags bindingAttr, MemberFilter? filter, object? filterCriteria)
        {
            // Define the work arrays
            MethodInfo?[]? m = null;
            ConstructorInfo?[]? c = null;
            FieldInfo?[]? f = null;
            PropertyInfo?[]? p = null;
            EventInfo?[]? e = null;
            Type?[]? t = null;
 
            int i;
            int cnt = 0;            // Total Matchs
 
            // Check the methods
            if ((memberType & MemberTypes.Method) != 0)
            {
                m = GetMethods(bindingAttr);
                if (filter != null)
                {
                    for (i = 0; i < m.Length; i++)
                        if (!filter(m[i]!, filterCriteria))
                            m[i] = null;
                        else
                            cnt++;
                }
                else
                {
                    cnt += m.Length;
                }
            }
 
            // Check the constructors
            if ((memberType & MemberTypes.Constructor) != 0)
            {
                c = GetConstructors(bindingAttr);
                if (filter != null)
                {
                    for (i = 0; i < c.Length; i++)
                        if (!filter(c[i]!, filterCriteria))
                            c[i] = null;
                        else
                            cnt++;
                }
                else
                {
                    cnt += c.Length;
                }
            }
 
            // Check the fields
            if ((memberType & MemberTypes.Field) != 0)
            {
                f = GetFields(bindingAttr);
                if (filter != null)
                {
                    for (i = 0; i < f.Length; i++)
                        if (!filter(f[i]!, filterCriteria))
                            f[i] = null;
                        else
                            cnt++;
                }
                else
                {
                    cnt += f.Length;
                }
            }
 
            // Check the Properties
            if ((memberType & MemberTypes.Property) != 0)
            {
                p = GetProperties(bindingAttr);
                if (filter != null)
                {
                    for (i = 0; i < p.Length; i++)
                        if (!filter(p[i]!, filterCriteria))
                            p[i] = null;
                        else
                            cnt++;
                }
                else
                {
                    cnt += p.Length;
                }
            }
 
            // Check the Events
            if ((memberType & MemberTypes.Event) != 0)
            {
                e = GetEvents(bindingAttr);
                if (filter != null)
                {
                    for (i = 0; i < e.Length; i++)
                        if (!filter(e[i]!, filterCriteria))
                            e[i] = null;
                        else
                            cnt++;
                }
                else
                {
                    cnt += e.Length;
                }
            }
 
            // Check the Types
            if ((memberType & MemberTypes.NestedType) != 0)
            {
                t = GetNestedTypes(bindingAttr);
                if (filter != null)
                {
                    for (i = 0; i < t.Length; i++)
                        if (!filter(t[i]!, filterCriteria))
                            t[i] = null;
                        else
                            cnt++;
                }
                else
                {
                    cnt += t.Length;
                }
            }
 
            // Allocate the Member Info
            MemberInfo[] ret = new MemberInfo[cnt];
 
            // Copy the Methods
            cnt = 0;
            if (m != null)
            {
                for (i = 0; i < m.Length; i++)
                    if (m[i] != null)
                        ret[cnt++] = m[i]!;
            }
 
            // Copy the Constructors
            if (c != null)
            {
                for (i = 0; i < c.Length; i++)
                    if (c[i] is ConstructorInfo ci)
                        ret[cnt++] = ci;
            }
 
            // Copy the Fields
            if (f != null)
            {
                for (i = 0; i < f.Length; i++)
                    if (f[i] is FieldInfo fi)
                        ret[cnt++] = fi;
            }
 
            // Copy the Properties
            if (p != null)
            {
                for (i = 0; i < p.Length; i++)
                    if (p[i] is PropertyInfo pi)
                        ret[cnt++] = pi;
            }
 
            // Copy the Events
            if (e != null)
            {
                for (i = 0; i < e.Length; i++)
                    if (e[i] is EventInfo ei)
                        ret[cnt++] = ei;
            }
 
            // Copy the Types
            if (t != null)
            {
                for (i = 0; i < t.Length; i++)
                    if (t[i] is Type type)
                        ret[cnt++] = type;
            }
 
            return ret;
        }
 
        public virtual bool IsSubclassOf(Type c)
        {
            Type? p = this;
            if (p == c)
                return false;
            while (p != null)
            {
                if (p == c)
                    return true;
                p = p.BaseType;
            }
            return false;
        }
 
        [Intrinsic]
        public virtual bool IsAssignableFrom([NotNullWhen(true)] Type? c)
        {
            if (c == null)
                return false;
 
            if (this == c)
                return true;
 
            // For backward-compatibility, we need to special case for the types
            // whose UnderlyingSystemType are runtime implemented.
            Type toType = this.UnderlyingSystemType;
            if (toType is RuntimeType)
                return toType.IsAssignableFrom(c);
 
            // If c is a subclass of this class, then c can be cast to this type.
            if (c.IsSubclassOf(this))
                return true;
 
            if (this.IsInterface)
            {
                return c.ImplementInterface(this);
            }
            else if (IsGenericParameter)
            {
                Type[] constraints = GetGenericParameterConstraints();
                for (int i = 0; i < constraints.Length; i++)
                    if (!constraints[i].IsAssignableFrom(c))
                        return false;
 
                return true;
            }
 
            return false;
        }
 
        // IL2085 is produced due to the "this" of the method not being annotated and used in effectively this.GetInterfaces()
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern",
            Justification = "The GetInterfaces technically requires all interfaces to be preserved" +
                "But this method only compares the result against the passed in ifaceType." +
                "So if ifaceType exists, then trimming should have kept it implemented on any type.")]
        internal bool ImplementInterface(Type ifaceType)
        {
            Type? t = this;
            while (t != null)
            {
                // IL2075 is produced due to the BaseType not returning annotated value and used in effectively this.BaseType.GetInterfaces()
                // The GetInterfaces technically requires all interfaces to be preserved
                // But this method only compares the result against the passed in ifaceType.
                // So if ifaceType exists, then trimming should have kept it implemented on any type.
                // The warning is currently analyzer only.
#pragma warning disable IL2075
                Type[] interfaces = t.GetInterfaces();
#pragma warning restore IL2075
                if (interfaces != null)
                {
                    for (int i = 0; i < interfaces.Length; i++)
                    {
                        // Interfaces don't derive from other interfaces, they implement them.
                        // So instead of IsSubclassOf, we should use ImplementInterface instead.
                        if (interfaces[i] == ifaceType ||
                            (interfaces[i] != null && interfaces[i].ImplementInterface(ifaceType)))
                            return true;
                    }
                }
 
                t = t.BaseType;
            }
 
            return false;
        }
 
        // FilterAttribute
        //  This method will search for a member based upon the attribute passed in.
        //  filterCriteria -- an Int32 representing the attribute
        private static bool FilterAttributeImpl(MemberInfo m, object filterCriteria)
        {
            // Check that the criteria object is an Integer object
            if (filterCriteria == null)
                throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt);
 
            switch (m.MemberType)
            {
                case MemberTypes.Constructor:
                case MemberTypes.Method:
                    {
                        MethodAttributes criteria;
                        try
                        {
                            int i = (int)filterCriteria;
                            criteria = (MethodAttributes)i;
                        }
                        catch
                        {
                            throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt);
                        }
 
 
                        MethodAttributes attr;
                        if (m.MemberType == MemberTypes.Method)
                            attr = ((MethodInfo)m).Attributes;
                        else
                            attr = ((ConstructorInfo)m).Attributes;
 
                        if (((criteria & MethodAttributes.MemberAccessMask) != 0) && (attr & MethodAttributes.MemberAccessMask) != (criteria & MethodAttributes.MemberAccessMask))
                            return false;
                        if (((criteria & MethodAttributes.Static) != 0) && (attr & MethodAttributes.Static) == 0)
                            return false;
                        if (((criteria & MethodAttributes.Final) != 0) && (attr & MethodAttributes.Final) == 0)
                            return false;
                        if (((criteria & MethodAttributes.Virtual) != 0) && (attr & MethodAttributes.Virtual) == 0)
                            return false;
                        if (((criteria & MethodAttributes.Abstract) != 0) && (attr & MethodAttributes.Abstract) == 0)
                            return false;
                        if (((criteria & MethodAttributes.SpecialName) != 0) && (attr & MethodAttributes.SpecialName) == 0)
                            return false;
                        return true;
                    }
                case MemberTypes.Field:
                    {
                        FieldAttributes criteria;
                        try
                        {
                            int i = (int)filterCriteria;
                            criteria = (FieldAttributes)i;
                        }
                        catch
                        {
                            throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt);
                        }
 
                        FieldAttributes attr = ((FieldInfo)m).Attributes;
                        if (((criteria & FieldAttributes.FieldAccessMask) != 0) && (attr & FieldAttributes.FieldAccessMask) != (criteria & FieldAttributes.FieldAccessMask))
                            return false;
                        if (((criteria & FieldAttributes.Static) != 0) && (attr & FieldAttributes.Static) == 0)
                            return false;
                        if (((criteria & FieldAttributes.InitOnly) != 0) && (attr & FieldAttributes.InitOnly) == 0)
                            return false;
                        if (((criteria & FieldAttributes.Literal) != 0) && (attr & FieldAttributes.Literal) == 0)
                            return false;
#pragma warning disable SYSLIB0050 // Legacy serialization infrastructure is obsolete
                        if (((criteria & FieldAttributes.NotSerialized) != 0) && (attr & FieldAttributes.NotSerialized) == 0)
                            return false;
#pragma warning restore SYSLIB0050
                        if (((criteria & FieldAttributes.PinvokeImpl) != 0) && (attr & FieldAttributes.PinvokeImpl) == 0)
                            return false;
                        return true;
                    }
            }
 
            return false;
        }
 
        // FilterName
        // This method will filter based upon the name.  A partial wildcard
        //  at the end of the string is supported.
        //  filterCriteria -- This is the string name
        private static bool FilterNameImpl(MemberInfo m, object filterCriteria, StringComparison comparison)
        {
            // Check that the criteria object is a String object
            if (filterCriteria is not string filterCriteriaString)
            {
                throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString);
            }
 
            ReadOnlySpan<char> str = filterCriteriaString.AsSpan().Trim();
            ReadOnlySpan<char> name = m.Name;
 
            // Get the nested class name only, as opposed to the mangled one
            if (m.MemberType == MemberTypes.NestedType)
            {
                name = name.Slice(name.LastIndexOf('+') + 1);
            }
 
            // Check to see if this is a prefix or exact match requirement
            if (str.EndsWith('*'))
            {
                str = str.Slice(0, str.Length - 1);
                return name.StartsWith(str, comparison);
            }
 
            return name.Equals(str, comparison);
        }
    }
}