C# 委托是否比类更轻?
我试图分解一个C#创建的可执行文件,但没有得出结论。我想知道的是,对于CLR c#,委托是真正的特殊实体还是仅仅是一个编译器 我问这个问题是因为我正在实现一种编译成C#的语言,对我来说,将匿名函数编译为类比作为委托有趣得多。但我不想使用一种我以后会后悔的设计,因为它们可能会占用更多的内存(我认为Java的PermGen是我提问的基础,尽管我知道CLR没有这种东西) 谢谢大家! --编辑 更清楚一点,我想知道以下两者之间是否有(以及有什么)区别:C# 委托是否比类更轻?,c#,delegates,clr,internals,il,C#,Delegates,Clr,Internals,Il,我试图分解一个C#创建的可执行文件,但没有得出结论。我想知道的是,对于CLR c#,委托是真正的特殊实体还是仅仅是一个编译器 我问这个问题是因为我正在实现一种编译成C#的语言,对我来说,将匿名函数编译为类比作为委托有趣得多。但我不想使用一种我以后会后悔的设计,因为它们可能会占用更多的内存(我认为Java的PermGen是我提问的基础,尽管我知道CLR没有这种东西) 谢谢大家! --编辑 更清楚一点,我想知道以下两者之间是否有(以及有什么)区别: void Main() { Func<
void Main()
{
Func<int, int, int> add = delegate(int a, int b) {return a + b;};
}
--编辑
我认为这两者之间可能有很大的区别:
class Program
{
static int add(int a, int b)
{
return a + b;
}
static void Main()
{
Func<int, int, int> add = Program.add;
}
}
不过,我在某处读到,语法Func add=Program.add
只是Func add=delegate(inta,intb){return Program.add;}的一个糖代码>。但我真的不知道这是不是真的。
我还可以看到,C#编译器已经缓存了所有这些实例,因此它们只构造一次。不过,我可以用我的编译器做同样的事情。两者之间似乎没有什么区别,上面的代码段实际上被编译成了与下面代码段几乎相同的东西。委托是类。NET编译器为每个委托创建一个类型,代码必须实例化一个委托
在任何情况下,性能上的差异都可以忽略不计,除非您正在编写一个非常罕见的应用程序,其中每一纳秒都很重要。我很惊讶您无法通过分解可执行文件得出结论。让我们来看一件非常简单的事情:
using System;
class A
{
int _x;
public A(int x)
{
_x = x;
}
public void Print(int y)
{
Console.WriteLine(_x + y);
}
}
interface IPseudoDelegateVoidInt
{
void Call(int y);
}
class PseudoDelegateAPrint : IPseudoDelegateVoidInt
{
A _target;
public PseudoDelegateAPrint(A target)
{
_target = target;
}
public void Call(int y)
{
_target.Print(y);
}
}
class Program
{
delegate void RealVoidIntDelegate(int x);
static void Main()
{
A a = new A(5);
IPseudoDelegateVoidInt pdelegate = new PseudoDelegateAPrint(a);
RealVoidIntDelegate rdelegate = new RealVoidIntDelegate(a.Print);
pdelegate.Call(2);
rdelegate(2);
}
}
如果我们把它拆开,我们会得到
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.1
// Copyright (c) Microsoft Corporation. All rights reserved.
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly del
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module del.exe
// MVID: {87A2A843-A5F2-4D40-A96D-9940579DE26E}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x0000000000B60000
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit A
extends [mscorlib]System.Object
{
.field private int32 _x
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 x) cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld int32 A::_x
IL_000f: nop
IL_0010: ret
} // end of method A::.ctor
.method public hidebysig instance void
Print(int32 y) cil managed
{
// Code size 16 (0x10)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 A::_x
IL_0007: ldarg.1
IL_0008: add
IL_0009: call void [mscorlib]System.Console::WriteLine(int32)
IL_000e: nop
IL_000f: ret
} // end of method A::Print
} // end of class A
.class interface private abstract auto ansi IPseudoDelegateVoidInt
{
.method public hidebysig newslot abstract virtual
instance void Call(int32 y) cil managed
{
} // end of method IPseudoDelegateVoidInt::Call
} // end of class IPseudoDelegateVoidInt
.class private auto ansi beforefieldinit PseudoDelegateAPrint
extends [mscorlib]System.Object
implements IPseudoDelegateVoidInt
{
.field private class A _target
.method public hidebysig specialname rtspecialname
instance void .ctor(class A target) cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld class A PseudoDelegateAPrint::_target
IL_000f: nop
IL_0010: ret
} // end of method PseudoDelegateAPrint::.ctor
.method public hidebysig newslot virtual final
instance void Call(int32 y) cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class A PseudoDelegateAPrint::_target
IL_0007: ldarg.1
IL_0008: callvirt instance void A::Print(int32)
IL_000d: nop
IL_000e: ret
} // end of method PseudoDelegateAPrint::Call
} // end of class PseudoDelegateAPrint
.class private auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{
.class auto ansi sealed nested private RealVoidIntDelegate
extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname
instance void .ctor(object 'object',
native int 'method') runtime managed
{
} // end of method RealVoidIntDelegate::.ctor
.method public hidebysig newslot virtual
instance void Invoke(int32 x) runtime managed
{
} // end of method RealVoidIntDelegate::Invoke
.method public hidebysig newslot virtual
instance class [mscorlib]System.IAsyncResult
BeginInvoke(int32 x,
class [mscorlib]System.AsyncCallback callback,
object 'object') runtime managed
{
} // end of method RealVoidIntDelegate::BeginInvoke
.method public hidebysig newslot virtual
instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
{
} // end of method RealVoidIntDelegate::EndInvoke
} // end of class RealVoidIntDelegate
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 45 (0x2d)
.maxstack 3
.locals init (class A V_0,
class IPseudoDelegateVoidInt V_1,
class Program/RealVoidIntDelegate V_2)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: newobj instance void A::.ctor(int32)
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: newobj instance void PseudoDelegateAPrint::.ctor(class A)
IL_000e: stloc.1
IL_000f: ldloc.0
IL_0010: ldftn instance void A::Print(int32)
IL_0016: newobj instance void Program/RealVoidIntDelegate::.ctor(object,
native int)
IL_001b: stloc.2
IL_001c: ldloc.1
IL_001d: ldc.i4.2
IL_001e: callvirt instance void IPseudoDelegateVoidInt::Call(int32)
IL_0023: nop
IL_0024: ldloc.2
IL_0025: ldc.i4.2
IL_0026: callvirt instance void Program/RealVoidIntDelegate::Invoke(int32)
IL_002b: nop
IL_002c: ret
} // end of method Program::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class Program
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file C:\Users\logan\del.res
如您所见,RealVoidIntDelegate变成了“仅仅”另一个类。他们称它为Invoke
,而不是Call
,他们没有使用接口,但没有涉及特殊的指令,这是相同的基本思想
为了详细说明“轻量级”的概念,委托并不比类轻,因为给定的委托是一个类。特别是在函数文字语法的情况下,它们的重量实在是太轻了。然而,对于“使用这些参数调用此对象上的此方法”案例,在编译到C#时,调用和创建给定的委托可能比生成的委托要轻。这里没有什么神秘之处。通灵乔恩·斯基特
如果您看一下的6.5.3,您将看到编译器如何处理匿名委托的几个示例。简要概述:
如果匿名方法未捕获外部变量,
然后可以将其创建为封闭类型上的静态方法
如果匿名方法引用封闭类型的成员,例如this.x
然后可以将其创建为封闭类型上的实例方法
如果匿名方法捕获局部变量,
然后可以在封闭类型中创建嵌套类型,实例变量与捕获的变量匹配,实例方法与委托类型匹配
最后一个示例更复杂,只是更容易查看代码。看看你自己,我想它会消除所有的猜测。除了元数据,一旦一切都得到了解决,就真的没有类了
JIT编译。对象的运行时表示是一个大字节数组
足够存储类的所有字段和
它是基类,加上一个存储
哈希代码、数字类型标识符和指向共享
保存虚拟函数重写地址的v表
委托是一个对象,其类派生自System.delegate。它存储对象和函数指针对的数组
匿名函数是没有名称的函数。然而,它们也是
通常与名为闭包的对象关联,该对象包含在创建指向匿名函数的“匿名委托”的包含方法中定义的所有参数和局部变量。(实际上,它通常只包含那些实际访问的变量)
在任何情况下,将堆栈变量移动到堆中都允许匿名委托在其定义的堆栈框架之外活动
将匿名函数与其闭包相关联的最简单方法是使其成为编译器生成的闭包类的方法
因此,如果您有词汇闭包,您必须(至少在某些情况下)使用类来实现匿名函数
如果没有词法闭包,那么可以将“匿名函数”作为
具有编译器生成名称的常规函数,位于声明该函数的方法旁边。在语言支持词法闭包的情况下,这也是一个有用的优化,但不需要
一般来说,如果没有闭包支持,匿名函数是没有用的,所以如果没有闭包支持,我不会在您的语言中包含它们。是的,我承认我是一个IL新手。我尝试使用ILDasm,它给了我这样的输出://000043:IntOper add1=delegate(intx,inty);但我确信我选择了错误的拆卸选项;)无论如何,谢谢你的详细回答!但是看看你的拆卸,它引起了我的注意。似乎不是为每个匿名函数创建一个类,而是仅为委托类型declarationRight创建一个类。这就是糖分的来源。在C++/CLI中,您实际上会说gcnewrealvoidintdelegate(a,&a::Print)
,这可能是
using System;
class A
{
int _x;
public A(int x)
{
_x = x;
}
public void Print(int y)
{
Console.WriteLine(_x + y);
}
}
interface IPseudoDelegateVoidInt
{
void Call(int y);
}
class PseudoDelegateAPrint : IPseudoDelegateVoidInt
{
A _target;
public PseudoDelegateAPrint(A target)
{
_target = target;
}
public void Call(int y)
{
_target.Print(y);
}
}
class Program
{
delegate void RealVoidIntDelegate(int x);
static void Main()
{
A a = new A(5);
IPseudoDelegateVoidInt pdelegate = new PseudoDelegateAPrint(a);
RealVoidIntDelegate rdelegate = new RealVoidIntDelegate(a.Print);
pdelegate.Call(2);
rdelegate(2);
}
}
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.1
// Copyright (c) Microsoft Corporation. All rights reserved.
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly del
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module del.exe
// MVID: {87A2A843-A5F2-4D40-A96D-9940579DE26E}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x0000000000B60000
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit A
extends [mscorlib]System.Object
{
.field private int32 _x
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 x) cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld int32 A::_x
IL_000f: nop
IL_0010: ret
} // end of method A::.ctor
.method public hidebysig instance void
Print(int32 y) cil managed
{
// Code size 16 (0x10)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 A::_x
IL_0007: ldarg.1
IL_0008: add
IL_0009: call void [mscorlib]System.Console::WriteLine(int32)
IL_000e: nop
IL_000f: ret
} // end of method A::Print
} // end of class A
.class interface private abstract auto ansi IPseudoDelegateVoidInt
{
.method public hidebysig newslot abstract virtual
instance void Call(int32 y) cil managed
{
} // end of method IPseudoDelegateVoidInt::Call
} // end of class IPseudoDelegateVoidInt
.class private auto ansi beforefieldinit PseudoDelegateAPrint
extends [mscorlib]System.Object
implements IPseudoDelegateVoidInt
{
.field private class A _target
.method public hidebysig specialname rtspecialname
instance void .ctor(class A target) cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld class A PseudoDelegateAPrint::_target
IL_000f: nop
IL_0010: ret
} // end of method PseudoDelegateAPrint::.ctor
.method public hidebysig newslot virtual final
instance void Call(int32 y) cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class A PseudoDelegateAPrint::_target
IL_0007: ldarg.1
IL_0008: callvirt instance void A::Print(int32)
IL_000d: nop
IL_000e: ret
} // end of method PseudoDelegateAPrint::Call
} // end of class PseudoDelegateAPrint
.class private auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{
.class auto ansi sealed nested private RealVoidIntDelegate
extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname
instance void .ctor(object 'object',
native int 'method') runtime managed
{
} // end of method RealVoidIntDelegate::.ctor
.method public hidebysig newslot virtual
instance void Invoke(int32 x) runtime managed
{
} // end of method RealVoidIntDelegate::Invoke
.method public hidebysig newslot virtual
instance class [mscorlib]System.IAsyncResult
BeginInvoke(int32 x,
class [mscorlib]System.AsyncCallback callback,
object 'object') runtime managed
{
} // end of method RealVoidIntDelegate::BeginInvoke
.method public hidebysig newslot virtual
instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
{
} // end of method RealVoidIntDelegate::EndInvoke
} // end of class RealVoidIntDelegate
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 45 (0x2d)
.maxstack 3
.locals init (class A V_0,
class IPseudoDelegateVoidInt V_1,
class Program/RealVoidIntDelegate V_2)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: newobj instance void A::.ctor(int32)
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: newobj instance void PseudoDelegateAPrint::.ctor(class A)
IL_000e: stloc.1
IL_000f: ldloc.0
IL_0010: ldftn instance void A::Print(int32)
IL_0016: newobj instance void Program/RealVoidIntDelegate::.ctor(object,
native int)
IL_001b: stloc.2
IL_001c: ldloc.1
IL_001d: ldc.i4.2
IL_001e: callvirt instance void IPseudoDelegateVoidInt::Call(int32)
IL_0023: nop
IL_0024: ldloc.2
IL_0025: ldc.i4.2
IL_0026: callvirt instance void Program/RealVoidIntDelegate::Invoke(int32)
IL_002b: nop
IL_002c: ret
} // end of method Program::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class Program
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file C:\Users\logan\del.res