给出的答案(没有变化涉及价值类型)是正确的。当变量类型参数之一是值类型时,协变和逆变不起作用的原因如下。假设它确实起作用并显示事情发生严重错误:
Func<int> f1 =()=>123;
Func<object> f2 = f1; // Suppose this were legal.
object ob = f2();
好的,会发生什么? f2与f1参考相同。因此无论f1如何,f2都会。 f1做什么?它在堆栈中放置一个32位整数。这项任务做了什么?它需要堆栈中的内容并将其存储在变量“ob”中。
拳击教学在哪里?没有一个!我们只是将一个32位整数存储到存储器中,而不是一个整数,而是一个64位指针指向包含盒装整数的堆位置。所以你只是错过了堆栈,并用无效的引用破坏了变量的内容。这个过程很快就会消失。
那么拳击教学应该去哪里?编译器必须在某处生成一个装箱指令。在调用f2后无法执行,因为编译器认为f2返回已装箱的对象。它不能进入到f1的调用,因为f1返回一个int,而不是一个盒装的int。它不能在对f2的呼叫和对f1 的呼叫之间进行,因为它们是相同的代表;没有'之间'。
,我们可以在这里做的唯一的事情是使第二行实际上是说:
Func<object> f2 =()=>(object)f1();
,现在我们没有f1和f2之间的参考身份了,所以什么变化点?具有协变参考转换的整点是保留参考标识。
无论你如何切片,事情都会出现可怕的错误,并且无法解决问题。因此,最好的做法是首先使该功能非法。泛型委托类型不允许出现差异,其中值类型会变化。
更新:我应该在这里注意到我的答案,在VB中,你可以将int返回的委托转换为返回对象的委托。 VB简单地生成第二个委托,该委托将调用包装到第一个委托并将结果包装起来。 VB选择放弃参考转换保留对象标识的限制。
这说明了C#和VB的设计哲学中一个有趣的区别。在C#中,设计团队总是在思考:“编译器如何发现用户程序中可能存在的错误并引起他们的注意?”而VB团队正在思考“我们如何才能弄清楚用户可能会发生什么,并代表他们做这件事?”简而言之,C#哲学是“如果你看到某些东西,说些什么”,而VB哲学则是“尽我所指,而不是我说的”。两者都是完全合理的哲学;有趣的是,由于设计原理,看到两种具有几乎相同特征集的语言在这些小细节上有所不同。
应该是'.Where(x => IsNull(x))'? – 2010-11-04 11:50:27
@Joel Etherton:同样的事情(差不多)。 – leppie 2010-11-04 11:52:19
尝试使'IsNull'通用。废话,这就是你问的:) – leppie 2010-11-04 11:53:21