Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Open generic type inheritance dynamic identification in C#

Tags:

c#

generics

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).

like image 902
Jan Avatar asked Aug 06 '12 10:08

Jan


People also ask

What is an open generic type?

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.

What is generic MVC?

Generic is a class which allows the user to define classes and methods with the placeholder.

What is unbound generic type?

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).


1 Answers

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
like image 52
Christoffer Lette Avatar answered Nov 07 '22 17:11

Christoffer Lette