C#编译器奇怪与委托构造函数

lep*_*pie 9 c# compiler-construction constructor delegates

基于以下问题,我发现了c#编译器的一些奇怪行为.

以下是有效的C#:

static void K() {}

static void Main()
{
  var k = new Action(new Action(new Action(K))));
}
Run Code Online (Sandbox Code Playgroud)

我发现奇怪的是编译器'解构'传递的委托.

ILSpy输出如下:

new Action(new Action(new Action(null, ldftn(K)), ldftn(Invoke)).Invoke);
Run Code Online (Sandbox Code Playgroud)

可以看出,它会自动决定使用Invoke委托的方法.但为什么?

实际上,代码不清楚.我们是否有一个三重包装的委托(实际)或内部委托只是'复制'到外部委托(我最初的想法).

当然,如果意图就像编译器发出代码一样,那么应该写一下:

var k = new Action(new Action(new Action(K).Invoke).Invoke);
Run Code Online (Sandbox Code Playgroud)

与反编译代码类似.

任何人都可以证明这种"令人惊讶的"转变的原因吗?

更新:

我只能想到一个可能的用例; 委托类型转换.例如:

delegate void Baz();
delegate void Bar();
...
var k = new Baz(new Bar( new Action (K)));
Run Code Online (Sandbox Code Playgroud)

如果使用相同的委托类型,编译器可能会发出警告.

Jon*_*eet 5

规范(第7.6.10.5节)说:

  • 使用与E给出的委托实例相同的调用列表初始化新的委托实例.

现在假设编译器将其翻译成类似于您的建议:

new Action( a.Target, a.Method)
Run Code Online (Sandbox Code Playgroud)

这只会创建一个具有单个方法调用的调用列表的委托.对于多播代表,它会违反规范.

示例代码:

using System;

class Program
{
    static void Main(string[] args)
    {
        Action first = () => Console.WriteLine("First");
        Action second = () => Console.WriteLine("Second");

        Action both = first + second;
        Action wrapped1 =
            (Action) Delegate.CreateDelegate(typeof(Action),
                                             both.Target, both.Method);
        Action wrapped2 = new Action(both);

        Console.WriteLine("Calling wrapped1:");
        wrapped1();

        Console.WriteLine("Calling wrapped2:");
        wrapped2();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Calling wrapped1:
Second
Calling wrapped2:
First
Second
Run Code Online (Sandbox Code Playgroud)

如您所见,编译器的实际行为与规范相匹配 - 您建议的行为不符合规范.

部分原因在于有些奇怪的"有时是单演,有时是多演员"的性质Delegate,当然......