Delegates比类更轻量级吗?

时间:2011-02-19 17:36:59

标签: c# delegates clr internals il

我试过反汇编C#创建的可执行文件,但我无法得出结论。我想知道的是,如果CLR c#的代表真的是特殊实体或只是编译器糖吗?

我问这个是因为我正在实现一种编译成C#的语言,对于我来说,将匿名函数编译为类而不是代理将会更有趣。但是我不想使用后来我会后悔的设计,因为它们可能在内存上更重(我想起Java的PermGen是基于我的问题。尽管我知道CLR没有这样的东西。)< / p>

谢谢!

- 修改

更清楚一点,我想知道是否存在(和有什么)之间的区别:

void Main()
{
    Func<int, int, int> add = delegate(int a, int b) {return a + b;};
}

,例如

class AnonFuncion__1219023 : Fun3
{
    public override int invoke(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;
    }
}

class Function__432892 : Fun3
{
    public override int invoke(int a, int b)
    {
        return Program.add(a, b);
    }
}

我在某处读到,语法Func<int, int, int> add = Program.add;只是Func<int, int, int> add = delegate(int a, int b) { return Program.add; };的糖。但我真的不知道这是否真的如此。 我还可以看到C#编译器已经缓存了所有这些实例,因此它们只构造了一次。不过,我可以对我的编译器做同样的事情。

5 个答案:

答案 0 :(得分:7)

我很惊讶你无法通过反汇编可执行文件得出结论。让我们来看看非常简单的事情:

        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,并且他们没有使用界面,但没有涉及特殊说明,它是相同的基本想法。

为了详细阐述“轻量级”的概念,委托的重量不比类轻,因为给定的委托一个类。特别是在函数文字语法的情况下,它们的重量确实不能轻得多。但是,对于“使用这些args在此对象上调用此方法”,在编译为C#时,调用和创建给定委托的情况可能比本地代理更轻,但

答案 1 :(得分:3)

代表是课程。 .NET编译器为每个委托创建一个类型,代码必须实例化一个委托。

在任何情况下,性能差异都可以忽略不计,除非您正在撰写非常罕见的应用,其中每纳秒都很重要。

答案 2 :(得分:3)

这里没有什么大不了的。引导Jon Skeet ......

如果您查看C# Specification的6.5.3,您将看到编译器如何处理匿名委托的几个示例。简要总结:

IF 匿名方法不会捕获外部变量,
那么它可以在封闭类型上创建为静态方法。

IF 匿名方法引用封闭类型的成员,例如this.x
那么它可以作为封闭类型的实例方法创建。

IF 匿名方法捕获局部变量,
THEN 它可以在封闭类型中创建为嵌套类型,实例变量与捕获的变量匹配,以及与委托类型匹配的实例方法。

最后一个示例更复杂,查看代码更容易。看看你自己,我认为它将消除所有的猜测。

答案 3 :(得分:1)

两者之间的差异似乎很小,顶部代码段有效地编译成与底部代码段中的内容几乎相同的内容。

答案 4 :(得分:1)

除了元数据之外,一旦获得所有内容,就没有类似的东西 JIT编译。对象的运行时表示是一个大字节数组 足以存储该类的所有字段 它是基类,加上一个存储一个的对象头 哈希码,数字类型标识符和指向共享的指针 保存虚函数覆盖地址的v-table。

委托是一个对象,其类派生自System.Delegate。它存储一组对象和函数指针对。

匿名函数是一个没有名称的函数。但是,它们也是 通常与称为闭包的对象相关联,该对象包含创建指向匿名函数的“匿名委托”的包含方法中定义的所有参数和局部。 (实际上它通常只包含那些实际访问过的变量。)

在任何情况下,将堆栈变量移动到堆都允许匿名委托实现它的定义堆栈框架。

将匿名函数与其闭包关联起来的最简单方法是使其成为编译器生成的闭包类的方法。

因此,如果你有词法闭包,你必须(至少在某些情况下)使用一个类来实现匿名函数。

如果你没有词法闭包,那么你可以发出“匿名函数”作为 带有编译器生成名称的常规函数​​,在声明它的方法旁边。在语言支持词法闭包的情况下,这也是一个有用的优化,但不需要。

一般情况下,如果没有闭包支持,匿名函数就无用了,所以如果不支持闭包,我就不会用你的语言包含它们。