In C# 4, variance will work on delegate types that are constructed with compatible reference types. However it will never work on delegates where the type arguments are value types.
The reason is because we cannot do it without misaligning the stack. Consider your example. You have a Func<int, int>
. Suppose we allowed you to convert it to a Func<int, double>
. The former takes 4 bytes off the stack and puts 4 back on, causing a net stack change of 0. The latter takes 4 off and puts 8 on, causing a net stack change of 4 bytes. The caller of the function would be reading 8 bytes off the stack, but there are only 4 on the stack and the rest is garbage; the result would be garbage, the stack would be misaligned, and the runtime would eventually crash horribly.
Now, we could do the "conversion" by allocating a new method that fixes it up -- that takes the 4 byte int and turns it into an 8 byte double. But this leads to unexpected badness. When you say
Exception x = new Exception();
object y = x;
you expect that there will be ONE object -- you don't expect that converting x to object causes memory to be allocated, putting a wrapper around the thing. You expect that "x==y" will be true for reference comparison. If we automatically did fixups for conversions between incompatible delegate types, then conversion would (1) allocate memory, and (2) destroy reference equality. Both are repugnant to the whole idea of reference conversions; the whole point of reference conversions is that they are cheap, fast and preserve refential identity.
If you want to allocate a new delegate that fixes up the return value of an existing delegate, you are free to do so; that we make that explicit in the code, so that it is clear that referential identity is being destroyed, is a good thing.