为什么不将Func <...>和Action统一起来?

mjg*_*py3 14 c# types anonymous-function unification

我发现自己一直想通过一个Func在地方的一个回报,没有输入Action,例如

Func<int> DoSomething = ...;

Task.Run(DoSomething);
Run Code Online (Sandbox Code Playgroud)

在哪里,我真的不在乎的回报价值DoSomething.

但是,这些类型并不统一,我最终包装了这个调用

Task.Run(() => { DoSomething(); });
Run Code Online (Sandbox Code Playgroud)

有没有办法让这些类型统一而不包装?另外,有没有很好的设计理由为什么他们不统一?

Wor*_*red 6

您希望以下语句为真:

如果我有Func<T>,我应该能够在Action需要的地方使用它.

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

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

Eric Lippert在他的博客中回答了这个问题:

不应该"作废"被认为是所有可能的类型协变返回类型转换的方法,从群体之目的,委托类型的超类型?

他的回答是"不",因为这最终与CLI规范不兼容.该CLI规范要求返回值进入堆叠,所以void功能没有最终产生,而那些做返回的东西"啪"的指令,就产生一个"流行"的指令.如果某种方法有一个"动作"可能包含一个void函数或一个返回某些东西的函数,这在编译时是不知道的,编译器就不会知道是否生成"pop"指令.

他接着说:

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

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

如果我们假设(B),那么就会有一个突破性的变化,正如Eric Lippert在这个博客中解释的那样.从他的博客这个适应的例子中,如果有一个隐式转换从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));
    }
}
Run Code Online (Sandbox Code Playgroud)

(显然,这不是他们正在做的事情,因为这只适用于Func<int>而不是Func<T>,但它说明了问题.)

摘要

我认为你面临的问题是CLI规范的一个工件,当时可能并不是很好,而且我猜他们不想引入重大改变以允许隐式转换才能正常工作.