I'm trying to cast a contravariant delegate but for some reason I can only do it using the "as" operator.
interface MyInterface { }
delegate void MyFuncType<in InType>(InType input);
class MyClass<T> where T : MyInterface
{
public void callDelegate(MyFuncType<MyInterface> func)
{
MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error
MyFuncType<T> castFunc2 = func as MyFuncType<T>;
MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error
}
}
castFunc2 works fine but castFunc1 and castFunc3 cause the error:
Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>'
The MSDN article on the as operator states that castFunc2 and castFunc3 are "equivalent" so I don't understand how only one of them could cause an error. Another piece of this that is confusing me is that changing MyInterface from an interface to a class gets rid of the error.
Can anyone help me understand what is going on here? Thanks!
Add a constraint such that T must be a class.
class MyClass<T> where T: class, MyInterface
This gives the compiler enough information to know that T is convertible. You don't need the explicit cast either.
Variance only applies to reference types. T is allowed to be a value type without the constraint which breaks the compilers ability to prove that T is compatible for contravariance.
The reason the second statement works is because as
actually can perform a null conversion. For example:
class SomeClass { }
interface SomeInterface { }
static void Main(string[] args)
{
SomeClass foo = null;
SomeInterface bar = foo as SomeInterface;
}
Foo
is obviously not directly convertable to SomeInterface
, but it still succeeds because a null conversion can still take place. Your MSDN reference may be correct for most scenarios, but the generated IL code is very different which means they are fundamentally different from a technical perspective.