Sự khác biệt giữa Gọi và DynamicInvoke


128

Sự khác biệt giữa Invoke và DynamicInvoke trong các đại biểu là gì? Xin vui lòng cho tôi một số ví dụ mã giải thích sự khác biệt giữa hai phương thức đó.

Câu trả lời:


206

Khi bạn có một thể hiện đại biểu, bạn có thể biết loại chính xác hoặc bạn có thể biết rằng đó là một Delegate. Nếu bạn biết loại chính xác, bạn có thể sử dụng Invoke, rất nhanh - mọi thứ đã được xác thực trước. Ví dụ:

Func<int,int> twice = x => x * 2;
int i = 3;
int j = twice.Invoke(i);
// or just:
int j = twice(i);

Tuy nhiên! Nếu bạn chỉ biết rằng nó Delegatephải giải quyết các tham số vv một cách thủ công - điều này có thể liên quan đến việc bỏ hộp, v.v. - rất nhiều sự phản ánh đang diễn ra. Ví dụ:

Delegate slowTwice = twice; // this is still the same delegate instance
object[] args = { i };
object result = slowTwice.DynamicInvoke(args);

Lưu ý Tôi đã viết argstay dài để làm rõ rằng có object[]liên quan. Có rất nhiều chi phí phụ ở đây:

  • mảng
  • xác thực các đối số được thông qua là "phù hợp" cho thực tế MethodInfo
  • unboxing vv khi cần thiết
  • phản ánh-gọi
  • sau đó người gọi cần làm gì đó để xử lý giá trị trả về

Về cơ bản, tránh DynamicInvokekhi nào bạn có thể. Invokeluôn luôn thích hợp hơn, trừ khi tất cả những gì bạn có là a Delegatevà an object[].

Để so sánh hiệu suất, phần sau trong chế độ phát hành bên ngoài trình gỡ lỗi (exe console) in:

Invoke: 19ms
DynamicInvoke: 3813ms

Mã số:

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
Điều đó có nghĩa là trong trường hợp sử dụng trình biên dịch DynamicInvoke tạo ra nhiều mã IL hơn để xử lý lệnh gọi đại biểu?
testCoder

2
@testCoder không, nó sẽ sử dụng sự phản chiếu
Marc Gravell

@MarcGravell khi tôi thử điều này trong một phương thức đang gây ra sự kiện, tôi nhận được cuộc gọi phương thức đầu tiên là khoảng 0,7766 ms nhưng lần thứ hai mất khoảng 0,0568 ms. Khi lần đầu tiên được gọi, sẽ mất nhiều thời gian hơn DynamicInvoke hoặc ngược lại. Khi tôi thử ví dụ của bạn với 1 vòng lặp và nhìn vào ms Invoke: 0,0478ms, DynamicInvoke: 0,053ms. Tại sao bạn so sánh họ nhiều hơn 1 cuộc gọi? Và tại sao cái thứ nhất mất nhiều thời gian hơn chức năng gọi thứ hai?
uzay95

4
@ uzay95 Cuộc gọi đầu tiên đến phương thức khiến quá trình biên dịch JIT diễn ra bởi CLR - điều này áp dụng cho bất kỳ phương thức nào vào lần đầu tiên được gọi sau khi quá trình được bắt đầu. Trong loại kịch bản này, bạn có thể thực hiện một trong ba điều sau: (1) chạy phương thức nhiều lần để thời gian cho cuộc gọi đầu tiên trở nên không đáng kể trong kết quả cuối cùng, (2) không bắt đầu đo cho đến khi bạn 'đã gọi phương thức một lần hoặc (3) sử dụng ngen.exe (quá mức cần thiết). Bài đăng này giải thích nó đủ tốt ... stackoverflow.com/questions/4446203/ từ
Quanta

@ marc-gravell Bạn không cần tạo một mảng để truyền cho DynamicInvoke vì chữ ký phương thức của nó nêu từ khóa params cho tham số args.
zodo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.