基于following question ,我发现了 c# 编译器的一些奇怪行为。
以下是有效的 C#:
static void K() {}
static void Main()
{
var k = new Action(new Action(new Action(K))));
}
我确实觉得奇怪的是编译器“解构”了传递的委托(delegate)。
ILSpy输出如下:
new Action(new Action(new Action(null, ldftn(K)), ldftn(Invoke)).Invoke);
如您所见,它自动决定使用委托(delegate)的 Invoke
方法。但是为什么?
事实上,代码不清楚。我们有一个三重包裹的委托(delegate)(实际)还是内部委托(delegate)只是“复制”到外部委托(delegate)(我最初的想法)。
当然,如果意图就像编译器发出的代码,那么应该这样写:
var k = new Action(new Action(new Action(K).Invoke).Invoke);
类似于反编译后的代码。
谁能证明这种“令人惊讶”转变的原因?
更新:
我只能想到一个可能的用例;委托(delegate)类型转换。例如:
delegate void Baz();
delegate void Bar();
...
var k = new Baz(new Bar( new Action (K)));
如果使用相同的委托(delegate)类型,编译器或许应该发出警告。
最佳答案
规范(第 7.6.10.5 节)说:
- The new delegate instance is initialized with the same invocation list as the delegate instance given by E.
现在假设编译器将其翻译成类似于您建议的内容:
new Action( a.Target, a.Method)
那只会创建一个具有单个 方法调用的调用列表的委托(delegate)。对于多播委托(delegate),它会违反规范。
示例代码:
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();
}
}
输出:
Calling wrapped1:
Second
Calling wrapped2:
First
Second
如您所见,编译器的真实行为符合规范 - 您建议的行为不符合。
这部分是由于 Delegate
有点奇怪的“有时是单播,有时是多播”的性质,当然...
关于C# 编译器与委托(delegate)构造函数的奇怪之处,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8077719/