Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot cast delegate to a generic type T

Tags:

c#

.net

generics

Why can’t we cast an instance of a delegate to a generic type T?

Consider a utility method CreateDelegate that creates an instance of T, which is a delegate, i.e. a type derived from MulticastDelegate.

 T CreateDelegate<T>() {… }

Unfortunately generics does not allow to constraint T to a type derived from MulticastDelegate giving the following compilation error:

Constraint cannot be special class 'System.MulticastDelegate'

Nevertheless, this utility method is checking that T is compatible with MulticastDelegate and creating a delegate via Reflection through Delegate::CreateDelegate. But, if we try to cast the result of Delegate::CreateDelegate to T, we will get the following compilation error:

Cannot convert type 'System.Delegate' to 'T'

However, if I cast it first to object and then to T it will work fine:

T h = (T) ((object) Delegate.CreateDelegate(typeof(T), target, m));

Why can’t we directly cast delegate to T?

like image 428
Miguel Gamboa Avatar asked Feb 10 '16 17:02

Miguel Gamboa


1 Answers

The C# language imposes a static check whether a cast from type X to type Y is valid — i.e. whether it makes sense in that the compiler can (to certain degree) guarantee compatibility and reject errors that as clear on compile-time. An unconstrained generic type T and System.Delegate have nothing directly in common. However, when casted to object the compiler knows that every type is esentially an object, so it allows the cast. That doesn't mean a run-time type check won't fail in a particular case.

The as operator is a little bit more permissive, as it won't cause an exception for an otherwise-invalid cast. The compiler is also less-strict in applying static checks. In your particular case this is helpful as you can omit the intermediate cast to object and use as T. However, one this required is that as works on class types only, so you have to apply the where T : class constraint.

So the method then would look like this (simplified):

public T CreateDelegate<T>(…) where T : class
{
    return Delegate.CreateDelegate(typeof(T), …) as T;
}

As @usr suggested, a recommended reading is Eric Lippert's blog, e.g. this article on casts and type parameters.

like image 184
Ondrej Tucny Avatar answered Sep 30 '22 05:09

Ondrej Tucny