Jaka jest różnica między Invoke a DynamicInvoke w delegatach? Proszę podać mi przykład kodu wyjaśniający różnicę między tymi dwiema metodami.Różnica między Invoke a DynamicInvoke
102
A
Odpowiedz
160
Gdy masz instancję delegata, możesz znać dokładny typ lub możesz po prostu wiedzieć, że jest to Delegate
. Jeśli znasz dokładny typ, możesz użyć Invoke
, czyli bardzo szybko - wszystko jest już wstępnie sprawdzone. Na przykład:
Func<int,int> twice = x => x * 2;
int i = 3;
int j = twice.Invoke(i);
// or just:
int j = twice(i);
Jednak! Jeśli wiesz, że jest to Delegate
, musi ręcznie rozwiązać parametry itd. Może to wymagać rozpakowania, itp. - odbywa się wiele refleksji. Na przykład:
Delegate slowTwice = twice; // this is still the same delegate instance
object[] args = { i };
object result = slowTwice.DynamicInvoke(args);
Uwaga Pisałem o args
długą ręką, aby było jasne, że object[]
jest zaangażowany. Istnieje wiele dodatkowych kosztów tutaj:
- tablica
- walidację przekazywane argumenty są „pasować” do rzeczywistej
MethodInfo
- unboxing itp miarę potrzeby
- odbicie-powoływać
- wówczas osoba dzwoniąca musi coś zrobić, aby przetworzyć wartość zwrotu:
Zasadniczo unikaj DynamicInvoke
, gdy ... możesz. Invoke
jest zawsze preferowany, chyba że wszystko, co masz, to Delegate
i object[]
.
Dla porównania wydajności, co następuje w trybie zwolnienia poza debugera (exe konsoli) drukuje:
Invoke: 19ms
DynamicInvoke: 3813ms
Kod:
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);
Czy to oznacza, że w przypadku użycia DynamicInvoke kompilator produkuje więcej kodu IL do obsługi delegowania delegatów? – testCoder
@ testCoder nie, użyje refleksji –
Dzięki, bardzo pełne wyjaśnienie. – testCoder