为什么不委托值类型逆变工作? [英] Why doesn't delegate contravariance work with value types?

查看:134
本文介绍了为什么不委托值类型逆变工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这片段是不是在LINQPad编译。

This snippet is not compiled in LINQPad.

void Main()
{
    (new[]{0,1,2,3}).Where(IsNull).Dump();
}

static bool IsNull(object arg) { return arg == null; }

编译器的错误信息是:

The compiler's error message is:

没有过载'UserQuery.IsNull(对象)比赛代表System.Func

No overload for 'UserQuery.IsNull(object)' matches delegate 'System.Func'

它为一个字符串数组,但 INT [] 不起作用。它显然与拳击,但我想知道的细节。

It works for a string array, but doesn't work for int[]. It's apparently related to boxing, but I want to know the details.

推荐答案

给出的答案(没有变化,涉及值类型)是正确的。究其原因协方差和逆变时的不同类型的参数之一是值类型如下不起作用。假设它的工作,并显示事情的严重错误:

The answer given (that there is no variance involving value types) is correct. The reason covariance and contravariance do not work when one of the varying type arguments is a value type is as follows. Suppose it did work and show how things go horribly wrong:

Func<int> f1 = ()=>123;
Func<object> f2 = f1; // Suppose this were legal.
object ob = f2();

OK,会发生什么? f2是参考相同的F1。因此,无论F1的确,F2一样。什么是F1吗?它把堆栈上的32位整数。什么分配呢?它需要什么是栈并将其存储在变量转播上。

OK, what happens? f2 is reference-identical to f1. Therefore whatever f1 does, f2 does. What does f1 do? It puts a 32 bit integer on the stack. What does the assignment do? It takes whatever is on the stack and stores it in variable "ob".

在哪里拳击指令?有没有一个!我们只存储一个32位整数入库的期待不是一个整数,而是一个64位的指针,指向包含盒装整堆的位置。所以,你刚刚都对准堆栈和无效引用损坏变量的内容。不久过程中会得一无是处去了。

Where was the boxing instruction? There wasn't one! We just stored a 32 bit integer into storage that was expecting not an integer but rather a 64 bit pointer to a heap location containing a boxed integer. So you've just both misaligned the stack and corrupted the contents of the variable with an invalid reference. Soon the process will go down in flames.

那么,应该拳击指令去?编译器有什么地方产生一个拳击指令。在调用F2后它不能去,因为编译器认为,F2返回已装箱的对象。它不能在调用F1去,因为F1返回一个int,而不是一个装箱的int。它不能调用f2和通话之间进入F1的,因为它们是相同的委托;没有之间的。

So where should the boxing instruction go? The compiler has to generate a boxing instruction somewhere. It can't go after the call to f2, because the compiler believes that f2 returns an object that has already been boxed. It can't go in the call to f1 because f1 returns an int, not a boxed int. It can't go between the call to f2 and the call to f1 because they are the same delegate; there is no 'between'.

我们可以在这里做的唯一的事情就是使第二行实际上的意思是:

The only thing we could do here is make the second line actually mean:

Func<object> f2 = ()=>(object)f1();

现在我们没有f1和f2之间的参考身份了,所以的什么是方差的点?具有整点协的引用转换的是 preserve参考身份

and now we don't have reference identity between f1 and f2 anymore, so what is the point of variance? The whole point of having covariant reference conversions is to preserve reference identity.

不管你如何切它,事情可怕的错误,也没有办法解决它。因此,做的最好的事情是使功能摆在首位非法的;没有允许泛型委托类型差异,其中一个值类型将是不同的东西。

No matter how you slice it, things go horribly wrong and there is no way to fix it. Therefore the best thing to do is to make the feature illegal in the first place; there is no variance allowed on generic delegate types where a value type would be the thing that is varying.

更新:我应该在这里注意到,在我的答案,在VB中,你的可以的转换一个int-返回委托返回一个对象的委托。 VB简单地产生它包装调用第一代表和箱子,结果第二次委托。 VB选择放弃一个引用转换preserves对象身份的限制。

UPDATE: I should have noted here in my answer that in VB, you can convert an int-returning delegate to an object-returning delegate. VB simply produces a second delegate which wraps the call to the first delegate and boxes the result. VB chooses to abandon the restriction that a reference conversion preserves object identity.

这说明了C#和VB的设计理念一个有趣的差异。在C#中,设计团队总是想着编译器如何能找到什么很可能是在用户程序中的错误,并把它带到他们的注意?和VB团队在思考我们如何才能找出用户可能意味着发生,只是做代表他们?总之,C#的理念是:如果你看到什么就说什么,而VB理念是做什么我的意思是,不是我说的。两者都是完全合理的理念;有趣的是如何看到两种语言具有几乎相同的功能集于这些小细节,由于设计原则不同。

This illustrates an interesting difference in the design philosophies of C# and VB. In C#, the design team is always thinking "how can the compiler find what is likely to be a bug in the user's program and bring it to their attention?" and the VB team is thinking "how can we figure out what the user likely meant to happen and just do it on their behalf?" In short, the C# philosophy is "if you see something, say something", and the VB philosophy is "do what I mean, not what I say". Both are perfectly reasonable philosophies; it is interesting seeing how two languages that have almost identical feature sets differ in these small details due to design principles.

这篇关于为什么不委托值类型逆变工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆