ความแตกต่างระหว่าง 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จะดีกว่าเสมอเว้นแต่สิ่งที่คุณมีคือ a Delegateและobject[].

สำหรับการเปรียบเทียบประสิทธิภาพสิ่งต่อไปนี้ในโหมดรีลีสนอกดีบักเกอร์ (คอนโซล 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);

3
หมายความว่าในกรณีของการใช้งานคอมไพลเลอร์ DynamicInvoke จะสร้างรหัส IL มากขึ้นเพื่อจัดการการเรียกผู้ร่วมประชุมหรือไม่?
testCoder

2
@testCoder ไม่มันจะใช้การสะท้อน
Marc Gravell

@MarcGravell เมื่อฉันลองใช้วิธีนี้ในการเพิ่มเหตุการณ์ฉันได้รับการเรียกวิธีแรกใช้เวลาประมาณ 0,7766 มิลลิวินาที แต่วินาทีใช้เวลาประมาณ 0,0568 มิลลิวินาที เมื่อ Invoke ตัวแรกจะใช้เวลานานกว่า DynamicInvoke หรือในทางกลับกัน เมื่อฉันพยายามตัวอย่างเช่นคุณด้วย 1 วงและมองไปที่ Invoke: 0,0478ms, DynamicInvoke: 0,053msMS ทำไมคุณถึงเปรียบเทียบการโทรมากกว่า 1 ครั้ง แล้วทำไมอันแรกถึงใช้เวลานานกว่าการเรียกฟังก์ชันที่สอง?
uzay95

4
@ uzay95 การเรียกใช้เมธอดครั้งแรกทำให้การคอมไพล์ JIT เกิดขึ้นโดย CLR ซึ่งใช้กับเมธอดใด ๆ ในครั้งแรกที่เรียกใช้หลังจากที่กระบวนการเริ่มต้น ในสถานการณ์แบบนี้คุณสามารถทำสิ่งใดสิ่งหนึ่งจากสามสิ่ง: (1) เรียกใช้วิธีการหลาย ๆ ครั้งเพื่อให้เวลาที่ใช้ในการโทรครั้งแรกไม่มีนัยสำคัญในผลลัพธ์สุดท้าย (2) อย่าเริ่มการวัดผลจนกว่าคุณจะ ได้เรียกวิธีการนี้ครั้งเดียวหรือ (3) ใช้ ngen.exe (overkill) โพสต์นี้อธิบายได้ดีพอ ... stackoverflow.com/questions/4446203/…
Quanta

@ marc-gravell คุณไม่จำเป็นต้องสร้างอาร์เรย์เพื่อส่งผ่านไปยัง DynamicInvoke เนื่องจากลายเซ็นของเมธอดจะระบุ คีย์เวิร์ดparamsสำหรับพารามิเตอร์ args
zodo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.