开发者

Problems casting objects of type Func<T,T> in C#

开发者 https://www.devze.com 2022-12-15 18:52 出处:网络
Why doesn\'t this compile? I suppose there are ways to work around this; just cur开发者_C百科ious.

Why doesn't this compile? I suppose there are ways to work around this; just cur开发者_C百科ious.

Thanks!

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


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.


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.


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);
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号