为什么不要Func< ...>和行动统一?

时间:2015-10-15 13:57:49

标签: c# types anonymous-function unification

我发现自己经常想要传递Func并返回而没有输入代替Action,例如

Func<int> DoSomething = ...;

Task.Run(DoSomething);

其中,我并非真正关心DoSomething的返回值。

然而,这些类型并不统一,我最终还是打包了

Task.Run(() => { DoSomething(); });

有没有办法让这些类型在没有包装的情况下统一?此外,还有很好的设计理由说明他们为什么不统一?

2 个答案:

答案 0 :(得分:6)

您希望以下陈述成立:

  

如果我有Func<T>,我应该可以在需要Action的地方使用它。

这需要将Func<T> (A)分配给Action(B)隐式转换为Action

如果我们假设(A)需要T,可以是任何类型,可分配给void

Eric Lippert在his blog中回答了这个问题:

  

为了从方法组到委托类型的协变返回类型转换的目的,不应该将“void”视为所有可能类型的超类型吗?

他的回答是“不”,因为这最终与CLI规范不兼容。 CLI规范要求返回值在堆栈上,因此void函数不会最终生成“pop”指令,而那些返回某些内容的指令会生成“pop”指令。如果有某种方法可以包含一个“动作”,它可以包含一个void函数或一个返回某些东西的函数,这在编译时是未知的,编译器就不会知道是否生成“流行”指令。

他接着说:

  

如果CLI规范说“任何函数的返回值都传回'虚拟寄存器'”而不是将其压入堆栈,那么我们就可以使返回的void委托与返回任何东西的函数兼容。您始终可以忽略寄存器中的值。但这不是CLI指定的,所以这不是我们能做的。

换句话说,如果存在这个“虚拟寄存器”,其中存储了函数的返回值(可能在CLI规范中不存在),C#的编写者及其编译器可能已经完成了你想要的,但是他们不能,因为他们不能脱离CLI规范。

如果我们假设(B),就会有一个突破性的变化,正如Eric Lippert在this blog中解释的那样。如果有从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规范的一个工件,当时可能并不是很好,而且我猜他们不想引入重大修改以允许隐式转换为它工作

答案 1 :(得分:1)

CLI Standard

  

II.4.6.1委托签名兼容性

     

代表只能以可验证的方式绑定目标方法:

     
      
  1. 目标方法的签名是委托 - 可分配 - 委托的签名;
  2.         

    ...

         

    类型D的目标方法或委托是委托 - 可分配 - 类型D的委托,当且仅当以下所有情况适用时:

         
        
    1. T的返回类型U和D的返回类型V,V是 assignable-to U。
    2.