Разница в производительности вызвана разной производительностью Invoke()
(быстро) и DynamicInvoke()
(медленно). Взглянув на сгенерированный IL прямого вызова делегата с типом Func<object>
, вы увидите, что результирующий IL на самом деле вызовет метод Invoke()
:
static void TestInvoke(Func<object> func) {
func();
}
Приведенный выше код компилируется в код IL, который выглядит примерно так (в отладочной сборке):
.method private hidebysig static void TestInvoke(class [mscorlib]System.Func`1<object> func) cil managed {
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0 // func
IL_0002: callvirt instance !0/*object*/ class [mscorlib]System.Func`1<object>::Invoke()
IL_0007: pop
IL_0008: ret
} // end of method Program::TestInvoke
И метод Invoke()
намного быстрее, чем метод DynamicInvoke()
, так как в основном ему не нужно разрешать тип делегата (как это уже известно). Следующий ответ на другой вопрос объясняет разницу между Invoke()
и DynamicInvoke()
более подробно:
https://stackoverflow.com/a/12858434/6122062
Следующий очень упрощенный и, вероятно, не очень точный тест показывает огромную разницу в производительности. Как видите, я даже использую один и тот же делегат, просто вызываю его по-разному:
class Program {
static void Main(string[] args) {
var ex = Expression.Lambda<Func<object>>(Expression.New(typeof(object))).Compile();
Stopwatch timer = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) TestInvoke(ex);
Console.WriteLine($"Invoke():\t\t{timer.Elapsed.ToString()}");
timer = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) TestDynamicInvoke(ex);
Console.WriteLine($"DynamicInvoke():\t{timer.Elapsed.ToString()}");
Console.ReadKey(true);
}
static void TestInvoke(Func<object> func) {
func();
}
static void TestDynamicInvoke(Delegate deleg) {
deleg.DynamicInvoke();
}
}
Результаты на моем ПК дома с использованием сборки выпуска без подключенного отладчика (как уже упоминалось выше, я знаю, что этот простой тест может быть не очень точным, но он демонстрирует огромную разницу в производительности)
Invoke(): 00:00:00.0080935
DynamicInvoke(): 00:00:00.8382236