3

私の質問はこれです:

ConstructorInfo.Invoke呼び出しに対応するDynamicMethodオブジェクトを構築する場合、すべての (またはほとんどの) 型の引数に対処するには、どの型の IL を実装する必要がありますか?呼び出しを行う前に、引数の数が渡されますか?


バックグラウンド

私は IoC コンテナーの 3 回目の反復を行っており、現在、使用されている大量の時間を簡単に削減できる領域があるかどうかを把握するために、いくつかのプロファイリングを行っています。

私が気づいたことの 1 つは、具象型に解決するときに、最終的にConstructorInfo.Invokeを使用してコンストラクターが呼び出され、解決した引数の配列を渡すことになるということです。

私が気づいたのは、invoke メソッドにはかなりのオーバーヘッドがあるということです。これのほとんどは、私が行っているのと同じチェックの別の実装にすぎないのではないかと考えています。

たとえば、コードに一致するコンストラクターがあるため、渡した定義済みのパラメーター名、型、および値に一致するコンストラクターを見つけるために、この特定の呼び出し呼び出しができるはずのもので終わらない方法はありません。適切な数の引数、適切な順序、適切な型、および適切な値に対処するためです。

解決メソッドへの 100 万回の呼び出しを含むプロファイリング セッションを実行し、それを Invoke 呼び出しを模倣するDynamicMethod実装に置き換えると、プロファイリングのタイミングは次のようになりました。

  • ConstructorInfo.Invoke: 1973ms
  • ダイナミックメソッド: 93ms

これは、このプロファイリング アプリケーションの合計実行時間の約 20% を占めています。つまり、ConstructorInfo.Invoke 呼び出しを同じことを行う DynamicMethod に置き換えることで、基本的なファクトリ スコープのサービスを処理するときに 20% のランタイムを削減できます (つまり、すべての解決呼び出しはコンストラクター呼び出しで終了します)。

これはかなり重要なことであり、このコンテキストでコンストラクター用の安定した DynamicMethod ジェネレーターを構築するのにどれだけの作業が必要かを詳しく調べる必要があると思います。

したがって、動的メソッドはオブジェクト配列を受け取り、構築されたオブジェクトを返します。問題の ConstructorInfo オブジェクトは既にわかっています。

したがって、動的メソッドは次の IL で構成されるように見えます。

l001:    ldarg.0      ; the object array containing the arguments
l002:    ldc.i4.0     ; the index of the first argument
l003:    ldelem.ref   ; get the value of the first argument
l004:    castclass T  ; cast to the right type of argument (only if not "Object")
(repeat l001-l004 for all parameters, l004 only for non-Object types,
 varying l002 constant from 0 and up for each index)
l005:    newobj ci    ; call the constructor
l006:    ret

他に考慮すべきことはありますか?

アプリケーションを「アクセス制限モード」で実行している場合、動的メソッドの作成はおそらく利用できないことを認識していることに注意してください(脳がこれらの用語をあきらめない場合があります)が、その場合、それを簡単に検出して、オーバーヘッドとすべてを使用して、以前と同じように元のコンストラクターを呼び出します。

4

2 に答える 2

1

値型の場合、ステップ l004 はl004: unbox.any T.

生成する必要がある適切な IL を特定する最も簡単な方法は、テスト コードを使用して C# コンパイラによって生成されたものを確認することです。

static void Test(object[] args)
{
  TestTarget((string)args[0], (int)args[1], (DateTime?)args[2]);
}

static void TestTarget(string s, int i, DateTime? dt){}

コンパイルすると:

L_0000: ldarg.0
L_0001:ldc.i4.0
L_0002: ldelem.ref
L_0003: キャストクラス文字列
L_0008: ldarg.0
L_0009:ldc.i4.1
L_000a: ldelem.ref
L_000b: unbox.any int32
L_0010: ldarg.0
L_0011:ldc.i4.2
L_0012: ldelem.ref
L_0013: unbox.any [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>
L_0018: void Program::TestTarget(string, int32, valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>) を呼び出す
L_001d: 戻る
于 2009-12-29T16:41:07.920 に答える
0

リフレクションをより簡単に (そしてより速く) 操作できるようにするためのライブラリが用意されています。たとえば、Fasterflectは任意のコンストラクターを呼び出すための IL を生成できます。コンストラクターで使用する引数を渡すだけです。

// note: class must have constructor with (int,string,string) signature
object obj = someType.CreateInstance( new { id=1, name="jens", foo="bar" } );

ライブラリは、コンストラクターと完全に一致するパラメーターのセットがない場合に、呼び出す適切なコンストラクターをプローブすることもできます。

// try to map id, name and foo to constructor parameters
// allows changing the order and permit fallback to setting fields/properties
// e.g. might result in call to ctor(string,string) and set field "id"
object obj = someType.TryCreateInstance( new { id=1, name="jens", foo="bar" } );

免責事項:私は貢献者としてそのプロジェクトに携わっています。

于 2010-03-22T03:57:10.303 に答える