Example #1
0
        private static bool CanCastTo(this TypeInfo fromTypeInfo, TypeInfo toTypeInfo, FoundationTypes foundationTypes)
        {
            if (fromTypeInfo.Equals(toTypeInfo))
                return true;

            if (fromTypeInfo.IsArray)
            {
                if (toTypeInfo.IsInterface)
                    return fromTypeInfo.CanCastArrayToInterface(toTypeInfo, foundationTypes);

                Type toType = toTypeInfo.AsType();
                if (fromTypeInfo.IsSubclassOf(toType))
                    return true;  // T[] is castable to Array or Object.

                if (!toTypeInfo.IsArray) 
                    return false;

                int rank = fromTypeInfo.GetArrayRank();
                if (rank != toTypeInfo.GetArrayRank())
                    return false;

                bool fromTypeIsSzArray = fromTypeInfo.IsSzArray(foundationTypes);
                bool toTypeIsSzArray = toTypeInfo.IsSzArray(foundationTypes);
                if (fromTypeIsSzArray != toTypeIsSzArray)
                {
                    // T[] is assignable to T[*] but not vice-versa.
                    if (!(rank == 1 && !toTypeIsSzArray))
                    {
                        return false; // T[*] is not castable to T[]
                    }
                }

                TypeInfo toElementTypeInfo = toTypeInfo.GetElementType().GetTypeInfo();
                TypeInfo fromElementTypeInfo = fromTypeInfo.GetElementType().GetTypeInfo();
                return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, foundationTypes);
            }

            if (fromTypeInfo.IsByRef)
            {
                if (!toTypeInfo.IsByRef)
                    return false;

                TypeInfo toElementTypeInfo = toTypeInfo.GetElementType().GetTypeInfo();
                TypeInfo fromElementTypeInfo = fromTypeInfo.GetElementType().GetTypeInfo();
                return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, foundationTypes);
            }

            if (fromTypeInfo.IsPointer)
            {
                Type toType = toTypeInfo.AsType();
                if (toType.Equals(foundationTypes.SystemObject))
                    return true;  // T* is castable to Object.

                if (toType.Equals(foundationTypes.SystemUIntPtr))
                    return true;  // T* is castable to UIntPtr (but not IntPtr)

                if (!toTypeInfo.IsPointer)
                    return false;

                TypeInfo toElementTypeInfo = toTypeInfo.GetElementType().GetTypeInfo();
                TypeInfo fromElementTypeInfo = fromTypeInfo.GetElementType().GetTypeInfo();
                return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, foundationTypes);
            }

            if (fromTypeInfo.IsGenericParameter)
            {
                //
                // A generic parameter can be cast to any of its constraints, or object, if none are specified, or ValueType if the "struct" constraint is
                // specified.
                //
                // This has to be coded as its own case as TypeInfo.BaseType on a generic parameter doesn't always return what you'd expect.
                //
                Type toType = toTypeInfo.AsType();
                if (toType.Equals(foundationTypes.SystemObject))
                    return true;

                if (toType.Equals(foundationTypes.SystemValueType))
                {
                    GenericParameterAttributes attributes = fromTypeInfo.GenericParameterAttributes;
                    if ((attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
                        return true;
                }

                foreach (Type constraintType in fromTypeInfo.GetGenericParameterConstraints())
                {
                    if (constraintType.GetTypeInfo().CanCastTo(toTypeInfo, foundationTypes))
                        return true;
                }

                return false;
            }

            if (toTypeInfo.IsArray || toTypeInfo.IsByRef || toTypeInfo.IsPointer || toTypeInfo.IsGenericParameter)
                return false;

            if (fromTypeInfo.MatchesWithVariance(toTypeInfo, foundationTypes))
                return true;

            if (toTypeInfo.IsInterface)
            {
                foreach (Type ifc in fromTypeInfo.ImplementedInterfaces)
                {
                    if (ifc.GetTypeInfo().MatchesWithVariance(toTypeInfo, foundationTypes))
                        return true;
                }
                return false;
            }
            else
            {
                // Interfaces are always castable to System.Object. The code below will not catch this as interfaces report their BaseType as null. 
                if (toTypeInfo.AsType().Equals(foundationTypes.SystemObject) && fromTypeInfo.IsInterface)
                    return true;

                TypeInfo walk = fromTypeInfo;
                for (;;)
                {
                    Type baseType = walk.BaseType;
                    if (baseType == null)
                        return false;
                    walk = baseType.GetTypeInfo();
                    if (walk.MatchesWithVariance(toTypeInfo, foundationTypes))
                        return true;
                }
            }
        }