我在玩 delegates
并注意到当我创建一个 Func<int,int,int>
像下面的例子:
Func<int, int, int> func1 = (x, y) => x * y;
编译器生成方法的签名不是我所期望的:
如您所见,它的第一个参数是一个对象。但是当有关闭时:
int z = 10;
Func<int, int, int> func1 = (x, y) => x * y * z;
一切如期进行:
这是带有额外参数的方法的 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/