Some strange behavior with the C# 4.0 co- and contravariance support:
using System; class Program { static void Foo(object x) { } static void Main() { Action<string> action = _ => { }; // C# 3.5 supports static co- and contravariant method groups // conversions to delegates types, so this is perfectly legal: action += Foo; // since C# 4.0 much better supports co- and contravariance // for interfaces and delegates, this is should be legal too: action += new Action<object>(Foo); } }
It's results with ArgumentException: Delegates must be of the same type.
Strange, isn't it? Why Delegate.Combine()
(which is been called when performing +=
operation on the delegates) does not support co- and contravariance at runtime?
Moreover, I've found that BCL's System.EventHandler<TEventArgs>
delegate type does not has contravariant annotation on it's generic TEventArgs
parameter! Why? It's perfectly legal, TEventArgs
type used only at input position. Maybe there is no contravariant annotation because of it nicely hides the bug with the Delegate.Combine()
? ;)
p.s. All this affects the VS2010 RC and later versions.
Long story short: delegate combining is all messed up with respect to variance. We discovered this late in the cycle. We're working with the CLR team to see if we can come up with some way to make all the common scenarios work without breaking backwards compatibility, and so on, but whatever we come up with will probably not make it into the 4.0 release. Hopefully we'll get it all sorted out in some service pack. I apologize for the inconvenience.
Covariance and contravariance specifies inheritance relation between generic types. When you have covariance & contravariance, the classes G<A>
and G<B>
may be in some inheritance relationship depending on what A
and B
is. You can benefit from this when calling generic methods.
However, the Delegate.Combine
method is not generic and the documentation clearly says when the exception will be thrown:
ArgumentException
- Both a and b are notnull
reference (Nothing
in Visual Basic), and a and b are not instances of the same delegate type.
Now, Action<object>
and Action<string>
are certainly instances of a different delegate type (even though related via inheritance relationship), so according to the documentation, it will throw an exception. It sounds reasonable that the Delegate.Combine
method could support this scenario, but that's just a possible proposal (obviously this wasn't needed until now, because you cannot declare inherited delegates, so before co/contra-variance, no delegates had any inheritance relationship).
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