2012-10-12 18 views

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

Czy to oznacza, że ​​w przypadku użycia DynamicInvoke kompilator produkuje więcej kodu IL do obsługi delegowania delegatów? – testCoder

+1

@ testCoder nie, użyje refleksji –

+2

Dzięki, bardzo pełne wyjaśnienie. – testCoder