8

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

我问这个是因为我正在实现一种可以编译为 C# 的语言,而且将匿名函数编译为类比编译为委托对我来说更有趣。但是我不想使用以后会后悔的设计,因为它们可能会占用更多内存(我认为 Java 的 PermGen 可以作为我提问的基础。尽管我知道 CLR 没有这样的东西)。

谢谢!

- 编辑

为了更清楚一点,我想知道以下之间是否存在(以及有什么区别):

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# 编译器已经缓存了所有这些实例,因此它们只构造一次。不过,我可以用我的编译器做同样的事情。

4

5 回答 5

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

为了详细说明“轻量级”的概念,委托并不比类轻,因为给定的委托一个类。尤其是在函数字面量语法的情况下,它们真的不能轻得多。但是,对于“使用这些参数在此对象上调用此方法”的情况,在编译为 C# 时,调用和创建给定的委托可能会比自制委托更轻。

于 2011-02-19T18:04:09.917 回答
3

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

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

于 2011-02-19T18:03:14.610 回答
3

这里没有什么大奥秘。引导乔恩·斯基特……

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

如果匿名方法没有捕获外部变量,
可以将其创建为封闭类型的静态方法。

IF匿名方法引用封闭类型的成员,例如this.x
THEN可以将其创建为封闭类型的实例方法。

如果匿名方法捕获了一个局部变量,
那么它可以创建为封闭类型中的嵌套类型,实例变量与捕获的变量匹配,实例方法与委托类型匹配。

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

于 2011-02-19T18:13:38.907 回答
1

两者之间似乎几乎没有区别,顶部代码段有效地编译成与底部代码段几乎相同的内容。

于 2011-02-19T18:01:03.577 回答
1

除了元数据,一旦所有东西都被 JIT 编译,就真的没有类这样的东西了。对象的运行时表示是一个字节数组,其大小足以存储类的所有字段及其基类,以及一个存储哈希码、数字类型标识符和指向共享 v 的指针的对象头。 - 保存虚函数覆盖地址的表。

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

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

在任何情况下,将堆栈变量移动到堆中允许匿名委托超出其定义的堆栈帧。

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

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

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

一般来说,如果没有闭包支持,匿名函数是没有用的,所以如果不支持闭包,我不会费心将它们包含在您的语言中。

于 2011-02-19T18:38:56.980 回答