Різниця між Invoke та DynamicInvoke


128

Яка різниця між Invoke та DynamicInvoke у делегатів? Будь ласка, дайте мені приклад коду, який пояснює різницю між цими двома методами.

Відповіді:


206

Коли у вас є екземпляр делегата, ви можете знати точний тип, а можете просто знати, що це 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);

Примітка. Я написав argsдовгу руку, щоб зрозуміти, що object[]це стосується. Тут є багато додаткових витрат:

  • масив
  • перевірка переданих аргументів є "придатною" для фактичної MethodInfo
  • розпакування тощо за необхідності
  • рефлексія-виклик
  • тоді абоненту потрібно щось зробити для обробки повернутого значення

В основному, уникайте DynamicInvokeколи-небудь можете. Invokeзавжди кращий, якщо тільки у вас є Delegateі є object[].

Для порівняння продуктивності виводиться наступне у режимі випуску поза відладчиком (консольне програмне забезпечення):

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);

3
Чи означає це, що у разі використання компілятор DynamicInvoke виробляє більше коду IL для обробки виклику делегата?
testCoder

2
@testCoder ні, він використовуватиме роздуми
Марк Гравелл

@MarcGravell, коли я спробую це у способі, що викликає події, я отримую перший виклик методу, який займає близько 0,7766 мс, але другий - 0,0568 мс. Коли перший - Invoke, це займає більше часу, ніж DynamicInvoke або навпаки. Коли я спробував ваш приклад з 1 циклом і подивився на мс Invoke: 0,0478ms, DynamicInvoke: 0,053ms. Чому ви порівнюєте їх більше ніж 1 дзвінок? І чому перший займає більше часу, ніж другий виклик функції?
uzay95

4
@ uzay95 Перший виклик методу викликає компіляцію JIT за допомогою CLR - це стосується будь-якого методу, коли він викликається після запуску процесу. У такому сценарії ви можете виконати одну з трьох речей: (1) запустити метод кілька разів, щоб час, необхідний для першого дзвінка, став незначним для кінцевого результату, (2) не починайте вимірювати, поки після вас Визвали метод один раз або (3) використовуєте ngen.exe (overkill). Цей пост пояснює це досить добре ... stackoverflow.com/questions/4446203/…
Quanta

@ marc-grabll Вам не потрібно створювати масив, щоб перейти до DynamicInvoke, оскільки його метод підписує ключове слово params для параметра args.
зодо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.