Miguel Gamboa
Miguel Gamboa

Reputation: 9353

Cannot cast delegate to a generic type T

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?

Upvotes: 8

Views: 2157

Answers (1)

Ondrej Tucny
Ondrej Tucny

Reputation: 27962

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.

Upvotes: 14

Related Questions