// Internally callable version of the export method above. Has two additional parameters: // fBoxedSource : assume the source type is boxed so that value types and enums are // compatible with Object, ValueType and Enum (if applicable) // fAllowSizeEquivalence : allow identically sized integral types and enums to be considered // equivalent (currently used only for array element types) static bool AreTypesAssignableInternal(TypeInfo pSourceType, TypeInfo pTargetType, bool fBoxedSource, bool fAllowSizeEquivalence) { // // Are the types identical? // if (AreTypesEquivalentInternal(pSourceType, pTargetType)) return true; // // Handle cast to interface cases. // if (pTargetType.IsInterface) { // Value types can only be cast to interfaces if they're boxed. if (!fBoxedSource && pSourceType.IsValueType) return false; if (ImplementsInterface(pSourceType, pTargetType)) return true; // Are the types compatible due to generic variance? // if (pTargetType.HasGenericVariance && pSourceType.HasGenericVariance) if (pTargetType.IsGenericType && pSourceType.IsGenericType) return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); return false; } if (pSourceType.IsInterface) { // The only non-interface type an interface can be cast to is Object. return pTargetType.IsSystemObject(); } // // Handle cast to array cases. // if (pTargetType.IsArray) { if (pSourceType.IsArray) { if (pSourceType.GetElementType().GetTypeInfo().IsPointer) { // If the element types are pointers, then only exact matches are correct. // As we've already called AreTypesEquivalent at the start of this function, // return false as the exact match case has already been handled. // int** is not compatible with uint**, nor is int*[] oompatible with uint*[]. return false; } else { // Source type is also a pointer. Are the element types compatible? Note that using // AreTypesAssignableInternal here handles array covariance as well as IFoo[] . Foo[] // etc. Pass false for fBoxedSource since int[] is not assignable to object[]. return AreTypesAssignableInternal(pSourceType.GetElementType().GetTypeInfo(), pTargetType.GetElementType().GetTypeInfo(), false, true); } } // Can't cast a non-array type to an array. return false; } if (pSourceType.IsArray) { // Target type is not an array. But we can still cast arrays to Object or System.Array. return pTargetType.IsSystemObject() || pTargetType.IsSystemArray(); } // // Handle pointer cases // if (pTargetType.IsPointer) { if (pSourceType.IsPointer) { if (pSourceType.GetElementType().GetTypeInfo().IsPointer) { // If the element types are pointers, then only exact matches are correct. // As we've already called AreTypesEquivalent at the start of this function, // return false as the exact match case has already been handled. // int** is not compatible with uint**, nor is int*[] compatible with uint*[]. return false; } else { // Source type is also a pointer. Are the element types compatible? Note that using // AreTypesAssignableInternal here handles array covariance as well as IFoo[] . Foo[] // etc. Pass false for fBoxedSource since int[] is not assignable to object[]. return AreTypesAssignableInternal(pSourceType.GetElementType().GetTypeInfo(), pTargetType.GetElementType().GetTypeInfo(), false, true); } } return false; } else if (pSourceType.IsPointer) { return false; } // // Handle cast to other (non-interface, non-array) cases. // if (pSourceType.IsValueType) { // Certain value types of the same size are treated as equivalent when the comparison is // between array element types (indicated by fAllowSizeEquivalence). These are integer types // of the same size (e.g. int and uint) and the base type of enums vs all integer types of the // same size. if (fAllowSizeEquivalence && pTargetType.IsValueType) { if (ArePrimitveTypesEquivalentSize(pSourceType, pTargetType)) return true; // Non-identical value types aren't equivalent in any other case (since value types are // sealed). return false; } // If the source type is a value type but it's not boxed then we've run out of options: the types // are not identical, the target type isn't an interface and we're not allowed to check whether // the target type is a parent of this one since value types are sealed and thus the only matches // would be against Object, ValueType or Enum, all of which are reference types and not compatible // with non-boxed value types. if (!fBoxedSource) return false; } // // Are the types compatible via generic variance? // // if (pTargetType.HasGenericVariance && pSourceType.HasGenericVariance) if (pTargetType.IsGenericType && pSourceType.IsGenericType) { if (TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType)) return true; } // Is the source type derived from the target type? if (IsDerived(pSourceType, pTargetType)) return true; return false; }