c# - 为什么编译器在没有闭包的情况下为委托(delegate)添加额外的参数?

标签 c# delegates cil

我在玩 delegates并注意到当我创建一个 Func<int,int,int>像下面的例子:

Func<int, int, int> func1 = (x, y) => x * y;

编译器生成方法的签名不是我所期望的:

enter image description here

如您所见,它的第一个参数是一个对象。但是当有关闭时:

int z = 10;
Func<int, int, int> func1 = (x, y) => x * y * z;

一切如期进行:

enter image description here

这是带有额外参数的方法的 IL 代码:

    .method private hidebysig static int32  '<Main>b__0'(object A_0,
                                                     int32 x,
                                                     int32 y) cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       8 (0x8)
  .maxstack  2
  .locals init ([0] int32 V_0)
  IL_0000:  ldarg.1
  IL_0001:  ldarg.2
  IL_0002:  mul
  IL_0003:  stloc.0
  IL_0004:  br.s       IL_0006
  IL_0006:  ldloc.0
  IL_0007:  ret
} // end of method Program::'<Main>b__0'

好像参数A_0甚至没有使用。那么,object 的目的是什么?第一种情况下的参数?为什么有闭包的时候不加?

注意:如果您对标题有更好的想法,请随时进行编辑。

注2:我编译了Debug中的第一段代码和 Release模式,没有区别。但是我在 Debug 中编译了第二个模式以获得闭包行为,因为它优化了 Release 中的局部变量模式。

注意 3: 我正在使用 Visual Studio 2014 CTP .

编辑: 这是为 Main 生成的代码在第一种情况下:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       30 (0x1e)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Func`3<int32,int32,int32> func1)
  IL_0000:  nop
  IL_0001:  ldsfld     class [mscorlib]System.Func`3<int32,int32,int32> ConsoleApplication9.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
  IL_0006:  dup
  IL_0007:  brtrue.s   IL_001c
  IL_0009:  pop
  IL_000a:  ldnull
  IL_000b:  ldftn      int32 ConsoleApplication9.Program::'<Main>b__0'(object,
                                                                       int32,
                                                                       int32)
  IL_0011:  newobj     instance void class [mscorlib]System.Func`3<int32,int32,int32>::.ctor(object,
                                                                                             native int)
  IL_0016:  dup
  IL_0017:  stsfld     class [mscorlib]System.Func`3<int32,int32,int32> ConsoleApplication9.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
  IL_001c:  stloc.0
  IL_001d:  ret
} // end of method Program::Main

最佳答案

虽然这看起来非常令人惊讶,但快速搜索表明这是出于性能原因。

关于 a bug report about it ,它指出没有隐式 this 的委托(delegate)比具有隐式 this 的委托(delegate)慢得多,因为没有隐式 的委托(delegate)这需要在调用委托(delegate)时进行一些复杂的参数改组:

假设您调用 func1(1, 2)。这看起来像(伪代码,不是 CIL)

push func1
push 1
push 2
call Func<,,>::Invoke

当已知此 func1 绑定(bind)到一个采用两个 int 值的静态函数时,它就需要执行任何一个的等效操作

push arg.1
push arg.2
call method

arg.0 = arg.1
arg.1 = arg.2
jmp method

而当已知 func1 绑定(bind)到采用 null 和两个 int 值的静态函数时,它只需要执行等效的操作的

arg.0 = null
jmp method

因为环境已经完美地设置为输入采用引用类型和两个 int 值的函数。

是的,这是一种通常无关紧要的微观优化,但每个人都可以从中受益,包括那些在它确实重要的情况下的人。

关于c# - 为什么编译器在没有闭包的情况下为委托(delegate)添加额外的参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27725939/

相关文章:

c# - 使用NEST使用ElasticSearch 2.x搜索多种类型时,如何得到混合结果?

c# - 覆盖或重载自动完成追加规则

c# - Unity3d 应用程序作为用户控件

swift - 更改数据和 popViewController 后 TableView 不会重新加载

c# - 使用反射在 C# 中引发事件的单元测试

c# - 报告 Directory.GetFiles 的进度

ios - 在 "More"部分时没有调用 didSelectViewController

c# - 调用不带参数的方法的 IL 代码

f# - 是否可以强制 .NET F# 编译器在模块中生成 CIL 字段?

c# - 澄清 IL 生成的引用字符串的代码