基于following question,我发现了c#编译器的一些奇怪的行为。
以下是有效的C#:
static void K() {}
static void Main()
{
var k = new Action(new Action(new Action(K))));
}
我觉得奇怪的是编译器'解构'传递的委托。
ILSpy输出如下:
new Action(new Action(new Action(null, ldftn(K)), ldftn(Invoke)).Invoke);
可以看出,它会自动决定使用委托的Invoke
方法。但为什么呢?
实际上,代码不清楚。我们是否有一个三重包装的代表(实际)或者是内部代表只是'复制'到外部代表(我最初的想法)。
当然,如果意图就像编译器发出代码一样,那么应该写一下:
var k = new Action(new Action(new Action(K).Invoke).Invoke);
与反编译代码类似。
有人可以证明这种“令人惊讶的”转变的原因吗?
更新
我只能想到一个可能的用例;委托类型转换。例如:
delegate void Baz();
delegate void Bar();
...
var k = new Baz(new Bar( new Action (K)));
如果使用相同的委托类型,编译器可能会发出警告。
答案 0 :(得分:5)
规范(第7.6.10.5节)说:
- 使用与E给出的委托实例相同的调用列表初始化新的委托实例。
现在假设编译器将其翻译成类似于你的建议:
new Action( a.Target, a.Method)
那只会创建一个带有单个方法调用的调用列表的委托。对于多播代表,它会违反规范。
示例代码:
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
有点奇怪的“有时是单演,有时是多演员”,当然......
答案 1 :(得分:3)
当您尝试将委托视为方法时,编译器实际上使用委托的Invoke()
方法。因此,例如,下面的两行编译为完全相同的IL(都调用Invoke()
):
k();
k.Invoke();
我认为你看到的奇怪之处就是这样。委托构造函数需要一个方法(或者更确切地说,一个方法组),但它会获得一个委托。因此,它将其视为一种方法,并使用Invoke()
方法。
至于含义,它是调用委托调用实际方法的委托。您可以通过访问代理人的Method
和Target
属性来自行验证。对于最外层代表,Method
为Action.Invoke
且Target
为内部代表。
答案 2 :(得分:2)
动作委托有一个像这样的构造函数
public extern Action(object @object,IntPtr method);
由于K是静态方法,因此不需要将对象作为第一个参数传递给最内部的动作实例,因此它传递null
摘要
var action = new Action(K) => Action action = new Action(null, ldftn(K))
new Action(action) => new Action(action, ldftn(Action.Invoke))
我希望这可以解释发生了什么?