web-dev-qa-db-ja.com

InvokeとDynamicInvokeの違い

デリゲートでのInvokeとDynamicInvokeの違いは何ですか?その2つの方法の違いを説明するコード例を教えてください。

121
testCoder

デリゲートインスタンスがある場合、正確な型を知っている場合もあれば、単にDelegateであることを知っている場合もあります。正確なタイプがわかっている場合は、Invokeを使用できます。これは非常に高速です。すべてが事前に検証済みです。例えば:

Func<int,int> twice = x => x * 2;
int i = 3;
int j = twice.Invoke(i);
// or just:
int j = twice(i);

しかしながら! Delegateであることがわかっている場合は、パラメータなどを手動で解決する必要があります。これには、ボックス化解除などが含まれる可能性があります。多くのリフレクションが行われています。例えば:

Delegate slowTwice = twice; // this is still the same delegate instance
object[] args = { i };
object result = slowTwice.DynamicInvoke(args);

object[]が関係していることを明確にするために、argsロングハンドを作成しました。ここには多くの追加費用があります:

  • 配列
  • 渡された引数の検証は、実際のMethodInfoの「適合」です
  • 必要に応じて開梱など
  • 反射呼び出し
  • その後、呼び出し元は戻り値を処理するために何かをする必要があります

基本的に、DynamicInvokeはできる限り避けてください。 Invokeobject[]のみを持っている場合を除き、Delegateが常に推奨されます。

パフォーマンスを比較するために、デバッガー(コンソールexe)の外部のリリースモードで次を出力します。

Invoke: 19ms
DynamicInvoke: 3813ms

コード:

Func<int,int> twice = x => x * 2;
const int LOOP = 5000000; // 5M
var watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
    twice.Invoke(3);
}
watch.Stop();
Console.WriteLine("Invoke: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
    twice.DynamicInvoke(3);
}
watch.Stop();
Console.WriteLine("DynamicInvoke: {0}ms", watch.ElapsedMilliseconds);
192
Marc Gravell