/// <inheritdoc/> internal override bool IsAssignableFrom(TypeData sourceType, IsAssignableFromContext context) { if (base.IsAssignableFrom(sourceType, context)) { return(true); } if (context.OnlyReferenceAndIdentityConversions == false) { // See if there are any user-defined implicit conversions from the source type to the target type defined in new types: // (we want to check in new types because a breaking change in a new version due to a type change can be mitigated by // defining an implicit conversion in one of the types in the new version). if (DoesSourceTypeHaveImplicitOperatorOverloadToTarget(sourceType, context)) { return(true); } if (DoesTargetTypeHaveImplicitOperatorOverloadFromSource(sourceType, context)) { return(true); } } return(false); }
/// <summary> /// Gets the value indicating whether a variable of the current type is assignable from the specified source type. /// </summary> /// <param name="sourceType">The source type from which to test assignability to this type.</param> /// <param name="context">Information about the context of the IsAssignableFrom invocation.</param> /// <returns>True if a value of the source type is assignable to a variable of the current type.</returns> #endif internal override bool IsAssignableFrom(TypeData sourceType, IsAssignableFromContext context) { if (base.IsAssignableFrom(sourceType, context)) { return(true); } // From C# specification: (The implicit reference conversions are) // - From an array-type S with an element type Se to an array-type T with an element type Te, provided all of the following are true: // - S and T differ only in element type. In other words, S and T have the same number of dimensions. // - Both Se and Te are reference-types. // - An implicit reference conversion exists from Se to Te. if (this.ElementType.IsValueType == false) { int sourceRank; TypeData sourceElementType; if (sourceType.IsArray(out sourceRank, out sourceElementType) && sourceElementType.IsValueType == false && this.ArrayRank == sourceRank) { return(this.ElementType.IsAssignableFrom(sourceElementType, new IsAssignableFromContext(context.NewAssemblyFamily, context.IsSourceTypeOld, onlyReferenceAndIdentityConversions: true))); } } return(false); }
/// <summary> /// Indicates whether a variable of the current type is assignable from the specified source type. /// </summary> /// <param name="sourceType">The source type from which to test assignability to this type.</param> /// <param name="context">Information about the context of the IsAssignableFrom invocation.</param> /// <returns>True if a value of the source type is assignable to a variable of the current type.</returns> #endif internal virtual bool IsAssignableFrom(TypeData sourceType, IsAssignableFromContext context) { if (this.IsEquivalentTo(sourceType, context.NewAssemblyFamily, context.IsSourceTypeOld)) { return(true); } foreach (var implicitConversionType in sourceType.GetDirectImplicitConversions(context.OnlyReferenceAndIdentityConversions)) { if (this.IsAssignableFrom(implicitConversionType, context)) { return(true); } } return(false); }
/// <summary> /// Gets the value indicating whether a variable of the current type is assignable from the specified source type. /// </summary> /// <param name="sourceType">The source type from which to test assignability to this type.</param> /// <param name="context">Information about the context of the IsAssignableFrom invocation.</param> /// <returns>True if a value of the source type is assignable to a variable of the current type.</returns> #endif internal override bool IsAssignableFrom(TypeData sourceType, IsAssignableFromContext context) { if (base.IsAssignableFrom(sourceType, context)) { return(true); } // Test for covariance and contravariance if (this.SupportsVariantTypeParameters && sourceType.IsVarianceConvertibleTo(this, context)) { return(true); } // From C# specification: (The implicit reference conversions are) // - From a single-dimensional array type S[] to System.Collections.Generic.IList<T> and its base interfaces, provided that there is an implicit identity // or reference conversion from S to T. TypeData iListElementType; int arrayRank; TypeData arrayElementType; if (this.IsIListGenericType(out iListElementType) && sourceType.IsArray(out arrayRank, out arrayElementType) && arrayRank == 1) { return(iListElementType.IsAssignableFrom(arrayElementType, new IsAssignableFromContext(context.NewAssemblyFamily, context.IsSourceTypeOld, onlyReferenceAndIdentityConversions: true))); } if (context.OnlyReferenceAndIdentityConversions == false) { TypeData targetUnderlyingType; if (this.IsNullable(out targetUnderlyingType)) { // An implicit conversion from S? to T? TypeData sourceUnderlyingType; if (sourceType.IsNullable(out sourceUnderlyingType)) { return(targetUnderlyingType.IsAssignableFrom(sourceUnderlyingType, context)); } // An implicit conversion from S to T? return(targetUnderlyingType.IsAssignableFrom(sourceType, context)); } } return(false); }
/// <inheritdoc/> internal override bool IsAssignableFrom(TypeData sourceType, IsAssignableFromContext context) { if (base.IsAssignableFrom(sourceType, context)) { return(true); } // Test for covariance and contravariance if (SupportsVariantTypeParameters && sourceType.IsVarianceConvertibleTo(this, context)) { return(true); } if (IsIListGenericType(out TypeData iListElementType) && sourceType.IsArray(out int arrayRank, out TypeData arrayElementType) && arrayRank == 1) { return(iListElementType.IsAssignableFrom(arrayElementType, new IsAssignableFromContext(context.NewAssemblyFamily, context.IsSourceTypeOld, onlyReferenceAndIdentityConversions: true))); } if (context.OnlyReferenceAndIdentityConversions == false) { if (IsNullable(out TypeData targetUnderlyingType)) { // An implicit conversion from S? to T? if (sourceType.IsNullable(out TypeData sourceUnderlyingType)) { return(targetUnderlyingType.IsAssignableFrom(sourceUnderlyingType, context)); } // An implicit conversion from S to T? return(targetUnderlyingType.IsAssignableFrom(sourceType, context)); } } return(false); }
/// <inheritdoc/> internal override bool IsVarianceConvertibleTo(ConstructedGenericTypeData target, IsAssignableFromContext context) { if (target.TypeKind == TypeKind && target.GenericTypeDefinition == GenericTypeDefinition && target.GenericArguments.Count == GenericArguments.Count) { Debug.Assert( target.GenericArguments.Count == GenericArguments.Count, "Two constructed generic types from the same generic type definition should have the same number of generic arguments."); var genericParameters = target.GenericTypeDefinition.GenericParameters; for (int i = 0, count = genericParameters.Count; i < count; i++) { if (genericParameters[i].IsGenericTypeArgumentVariant(target.GenericArguments[i], GenericArguments[i], context) == false) { return(false); } } return(true); } return(false); }
private bool DoesTargetTypeHaveImplicitOperatorOverloadFromSource(TypeData sourceType, IsAssignableFromContext context) { var newTargetType = context.IsTargetTypeOld ? GetEquivalentNewType(context.NewAssemblyFamily) as DeclaringTypeData : this; if (newTargetType == null) { return(false); } var implicitOperators = newTargetType.GetMembers(OperatorData.ImplicitCastOperatorName) .OfType <OperatorData>() .Where(o => o.Type == newTargetType); foreach (var op in implicitOperators) { if (context.IsSourceTypeOld) { if (op.Parameters[0].Type.IsEquivalentToOld(sourceType, context.NewAssemblyFamily)) { return(true); } } else { if (op.Parameters[0].Type == sourceType) { return(true); } } } return(false); }
/// <summary> /// Determines whether the variance of the type parameter allows the source type in the parameter's position to vary to the target type when the declaring /// generic of one construction is trying to be converted to a another construction. /// </summary> /// <param name="targetArgument">The type in this parameter's position in the target constructed generic type.</param> /// <param name="sourceArgument">The type in this parameter's position in the source constructed generic type.</param> /// <param name="context">Information about the context of the IsAssignableFrom invocation.</param> internal bool IsGenericTypeArgumentVariant(TypeData targetArgument, TypeData sourceArgument, IsAssignableFromContext context) { var variance = GenericParameterAttributes & GenericParameterAttributes.VarianceMask; switch (variance) { case GenericParameterAttributes.None: return(targetArgument.IsEquivalentTo(sourceArgument, context.NewAssemblyFamily, context.IsSourceTypeOld)); case GenericParameterAttributes.Contravariant: return(sourceArgument.IsAssignableFrom(targetArgument, new IsAssignableFromContext(context.NewAssemblyFamily, context.IsSourceTypeOld, onlyReferenceAndIdentityConversions: true))); case GenericParameterAttributes.Covariant: return(targetArgument.IsAssignableFrom(sourceArgument, new IsAssignableFromContext(context.NewAssemblyFamily, context.IsSourceTypeOld, onlyReferenceAndIdentityConversions: true))); default: Debug.Fail("Unknown variance: " + variance); return(false); } }
/// <summary> /// Indicates whether the type can convert to the specified target type using type parameter variance. /// </summary> /// <param name="target">The target type to which this type might convert.</param> /// <param name="context">Information about the context of the IsAssignableFrom invocation.</param> /// <returns>True is the type is variance convertible to the target; False otherwise.</returns> #endif internal virtual bool IsVarianceConvertibleTo(ConstructedGenericTypeData target, IsAssignableFromContext context) { return(false); }