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は外側の型のインスタンス メソッドとして作成できます。

匿名メソッドがローカル変数をキャプチャする場合は、キャプチャされた変数に一致するインスタンス変数と、
デリゲート型に一致するインスタンス メソッドを使用して、外側の型内に入れ子になった Type として作成できます。

最後の例はより複雑で、コードを見るのが簡単です。自分で調べてみてください。当て推量はすべて排除されると思います。

于 2011-02-19T18:13:38.907 に答える
1

メタデータを除けば、すべてがJITコンパイルされると、クラスのようなものは実際にはありません。オブジェクトの実行時表現は、クラスとその基本クラスのすべてのフィールドを格納するのに十分な大きさのバイトの配列に加えて、ハッシュコード、数値型識別子、および共有vへのポインタを格納するオブジェクトヘッダーです。 -仮想関数オーバーライドのアドレスを保持するテーブル。

デリゲートはオブジェクトであり、そのクラスはSystem.Delegateから派生します。オブジェクトと関数のポインタペアの配列を格納します。

匿名関数は、名前のない関数です。ただし、これらは通常、クロージャと呼ばれるオブジェクトにも関連付けられています。このオブジェクトには、無名関数を指す「匿名デリゲート」を作成したcontainingメソッドで定義されたすべてのパラメーターとローカルが含まれます。(実際には、通常、実際にアクセスされる変数のみが含まれています)。

いずれの場合も、スタック変数をヒープに移動すると、匿名のデリゲートは、定義しているスタックフレームを存続させることができます。

匿名関数をそのクロージャに関連付ける最も簡単な方法は、それをコンパイラによって生成されたクロージャクラスのメソッドにすることです。

したがって、字句クロージャがある場合は、(少なくとも場合によっては)クラスを使用して無名関数を実装する必要があります。

字句クロージャがない場合は、「匿名関数」を、それを宣言したメソッドの横に、コンパイラによって生成された名前を持つ通常の関数として出力できます。これは、言語が字句クロージャをサポートしている場合に役立つ最適化でもありますが、クロージャは必要ありません。

一般に、無名関数はクロージャのサポートなしでは役に立ちません。そのため、クロージャのサポートなしでそれらをあなたの言語に含めることはしません。

于 2011-02-19T18:38:56.980 に答える
1

2 つの間にほとんど違いはないようです。上のスニペットは、下のスニペットにあるものとほぼ同じものに効果的にコンパイルされます。

于 2011-02-19T18:01:03.577 に答える