When I have two non-generic Type
objects a
and b
, I can easily verify whether a is assignable from b by using the a.IsAssignableFrom(b)
function.
class ClassBase { }
class ClassDerived : ClassBase { }
...
typeof(ClassBase).IsAssignableFrom(typeof(ClassDerived)) //returns true
Now say I have two generic interfaces:
interface IBase<T> { }
interface IDerived<T> : IBase<T> { }
If I close them down, I can do the same thing as before, with exactly the same behaviour, e.g.
typeof(IBase<object>).IsAssignableFrom(typeof(IDerived<object>)) //returns true
In fact, any T
that can be used to close down IDerived
can also be used to close down IBase
and IBase<T>
is assignable from IDerived<T>
(for that particular T).
However,
typeof(IBase<>).IsAssignableFrom(typeof(IDerived<>)) //returns false
I sort of have an idea why that might be so (they can be closed down on different types and thus get inconvertible?). I understand that a function that does return true in this case is thus somewhat different. The question is: "Is IBase<T>
assignable from IDerived<T>
for every valid T
?" (thanks to hvd)
What I thought of doing is closing the generics and then asking whether they are assignable. But to be general, I would need to close down under the most generic type(s) b
can take and that could be a) ugly, b) quite hard to do.
Another approach is to go up the implementation/inheritance tree on b
and try comparing it to a
.
My question is whether there is an easier way of doing this even in general cases.
Motivation: general interest, as I don't actually need this at the end. However, the initial need for this came while I was using Ninject with open and closed generics and I needed to resolve whether an open generic class can be cast to an open generic interface (class).
The C# language defines an open type to be a type that's either a type argument or a generic type defined with unknown type arguments: All types can be classified as either open types or closed types. An open type is a type that involves type parameters. More specifically: A type parameter defines an open type.
Generic is a class which allows the user to define classes and methods with the placeholder.
An unbound type refers to the entity declared by a type declaration. An unbound generic type is not itself a type, and it cannot be used as the type of a variable, argument, or return value, or as a base type. The only construct in which an unbound generic type can be referenced is the typeof expression (§7.6. 11).
As you've already found out, typeof(IBase<>).IsAssignableFrom(typeof(IDerived<>))
will never return true, since the two open generic types aren't in each others inheritance hierarchies.
My question is whether there is an easier way of doing this even in general cases.
No, not easier, but...
If T
doesn't have any constraints (where T: ...
) for either of the two generic types you're checking assignability for I think you might be able to construct the closed generic types by using object
as type parameter, and then using IsAssignableFrom
on the constructed types.
If T
is constrained on any of the generic types, you will have to use reflection to find those constraints (Type.GetGenericArguments, Type.GetGenericParameterConstraints), and then construct the generic types using that information. In this scenario, the constraining types must still be the same because of the A<T> : B<T>
inheritance (the same T
) in order to have the possibility of assignability between the two generic types. Note that if one constraining type is inheriting the other, you will find assignability of the generic types if you construct them both with the most derived of the two constraining types.
Here are some examples:
public class A<T> {}
public class B<T> : A<T> {}
public class C<T> where T: E {}
public class D<T> : C<T> where T: F {}
public class E {}
public class F : E {}
public class G : F {}
typeof(A<>).IsAssignableFrom(typeof(B<>)) // false
typeof(A<object>).IsAssignableFrom(typeof(B<object>)) // true
typeof(A<string>).IsAssignableFrom(typeof(B<string>)) // true
typeof(C<E>).IsAssignableFrom(typeof(D<F>)) // false
typeof(C<F>).IsAssignableFrom(typeof(D<F>)) // true
typeof(C<G>).IsAssignableFrom(typeof(D<G>)) // true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With