例如,我一直希望传递一个返回且没有输入的Func,而不是Action。
Func<int> DoSomething = ...;
Task.Run(DoSomething);在哪里,我并不真正关心DoSomething的返回值。
然而,这些类型并不统一,最后我结束了对调用的包装。
Task.Run(() => { DoSomething(); });有没有办法使这些类型在不包装的情况下统一?还有,它们没有统一的好的设计理由吗?
发布于 2015-10-15 15:03:49
您希望下列语句为真:
如果我有一个
Func<T>,我应该能够在需要Action的地方使用它。
这将需要Func<T>是(A)可分配给Action或(B)隐式可转换为Action。
如果我们假设(A)需要T (可以是任何类型),则可以分配给void。
Eric在他的博客中回答了这个问题
为了从方法组到委托类型的协变返回类型转换,不应该将“void”视为所有可能类型的超级类型吗?
他的回答是“不行”,因为这最终不符合CLI规范。CLI规范要求返回值必须放在堆栈上,这样void函数就不会生成"pop“指令,而返回某些内容的函数就会生成"pop”指令。如果有某种方法可以有一个包含void函数或返回在编译时不知道的函数的“操作”,那么编译器将不知道是否要生成"pop“指令。
他接着说:
如果CLI规范说“任何函数的返回值都在‘虚拟寄存器’中传回”,而不是将其推到堆栈上,那么我们就可以使返回的委托与返回任何内容的函数兼容。您可以始终忽略寄存器中的值。但这不是CLI指定的,所以这不是我们能做的。
换句话说,如果存在存储函数返回值的“虚拟寄存器”(大概在CLI规范中不存在),那么C#及其编译器的编写人员本来可以做您想做的事情,但是他们不能这样做,因为他们不能偏离CLI规范。
如果我们假设(B),就会发生重大变化,正如Eric在这个博客中所解释的那样。将他博客中的例子改编成这样,如果有一个从Func<T>到Action的隐式转换,一些程序将不再编译过去的程序(中断更改)。这个程序目前正在编译,但是尝试不注释隐式转换操作符,类似于您所要求的,它不编译。
public class FutureAction
{
public FutureAction(FutureAction action)
{
}
//public static implicit operator FutureAction(Func<int> f)
//{
// return new FutureAction(null);
//}
public static void OverloadedMethod(Func<FutureAction, FutureAction> a)
{
}
public static void OverloadedMethod(Func<Func<int>, FutureAction> a)
{
}
public static void UserCode()
{
OverloadedMethod(a => new FutureAction(a));
}
}(显然,这并不是他们要做的,因为这只适用于Func<int>,而不是Func<T>,但它说明了问题所在。)
摘要
我认为您所面临的问题是CLI规范的一个工件,这在当时可能是没有被忽略的,我猜他们不想引入破坏性的更改,以允许隐式转换才能正常工作。
发布于 2015-10-15 14:29:24
从CLI标准中拔出
二.4.6.1代表签名兼容性 只有在以下情况下,委托才能可验证地绑定到目标方法:
..。
T类型的目标方法或委托是委派给D类型的委托的当且仅当以下所有情况都适用:
https://stackoverflow.com/questions/33150506
复制相似问题