Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Co- and Contravariance bugs in .NET 4.0

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.

like image 353
controlflow Avatar asked Feb 21 '10 18:02

controlflow


2 Answers

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.

like image 121
Eric Lippert Avatar answered Oct 04 '22 16:10

Eric Lippert


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

like image 23
Tomas Petricek Avatar answered Oct 04 '22 18:10

Tomas Petricek