views:

117

answers:

3

Hi there,

Why doesn't this compile? I suppose there are ways to work around this; just curious.

Thanks!

    static void Main(string[] args)
    {
        Func<int, int> f_int = n => n * n;
        Func<int, double> f_double = (Func<int, double>)f_int;
    }
+5  A: 

Generics don't have this type of variance; not now, and not (for value-types such as int/double) in 4.0. Simply, f_int doesn't return a double. The best you can do is:

Func<int, double> f_double = i => f_int(i);

which has an implicit conversion from int to double in the returned value.

Marc Gravell
Thanks! - It just beats me that something as intuitive as this has not been implemented, as it makes it harder and more laborious to work with Funct<T,T> (I've just seen this: http://stackoverflow.com/questions/729295/how-to-cast-expressionfunct-datetime-to-expressionfunct-object)
d.
A: 

Delgates aren't convertable like this, since double doesn't inherit from int or visa versa .

The workarounds are just using a double to store the result in:

double r = f_int(a);

or using dynamic in C# 4.0:

Func<dynamic, dynamic> f = n => n*n;
double d = f(2.0);
int i = f(2);
codekaizen
That's good, but for my project (in 4.0), I need to work with the expressions and not just with the delegates, and this doesn't compile either:Expression<Func<dynamic, dynamic>> f = x => x * x;
d.
+5  A: 

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.

Eric Lippert
Thanks - I was curious about this. I had envisioned it was a stack management issue, but couldn't be sure... until now.
codekaizen