2012-12-05 37 views
18

Khi tôi muốn tự động gọi tĩnh được xác định ("tĩnh" theo nghĩa "được xác định tại thời gian biên dịch", không theo nghĩa "thành viên cấp lớp") trên bất kỳ đối tượng nào trong C#, tôi có thể sử dụng phản ánh để có được một xử lý cho phương pháp đó và gọi nó:Làm cách nào để tự động gọi một phương thức trên một đối tượng động?

typeof(Foo).GetMethod("Bar").Invoke(foo, new object[] { /* params */ }); 

Tuy nhiên, các đối tượng làm động bằng cách kế thừa từ DynamicObject đáp ứng với (không xác định) phương pháp dụ gọi sử dụng TryInvokeMember, và các phương pháp năng động lớp đáp ứng không tiếp xúc thông qua phản ánh, vì lý do rõ ràng. Điều này có nghĩa là tôi không thể xử lý phương thức cho phương thức sẽ được trả lời bởi TryInvokeMember. Vì vậy, trớ trêu thay, có vẻ như với tôi rằng bạn không thể tự động gọi một phương thức động trên một đối tượng dynamic dễ dàng như bạn có thể gọi một phương thức được xác định trên một đối tượng không phải là dynamic.

Tôi đã cân nhắc gọi trực tiếp số TryInvokeMember, nhưng đối số đầu tiên phải là một phiên bản của một lớp trừu tượng InvokeMemberBinder. Tôi cảm thấy rằng nếu tôi phải thực hiện một lớp học để gọi một phương pháp năng động trên một đối tượng năng động, tôi phải làm điều gì đó sai trái.

Làm thế nào tôi có thể gọi một phương thức trên một đối tượng dynamic theo tên của nó, biết rằng lớp mục tiêu không không thực hiện nó và nó nên được đáp lại bằng TryInvokeMember?

Trả lời

9

Một cách để thực hiện nó là bắt chước trình biên dịch C# đầu ra cho lời gọi phương thức trên các đối tượng động. Điều này yêu cầu sử dụng một loạt các loại được đánh dấu [EditorBrowsable(EditorBrowsableState.Never)] trong không gian tên Microsoft.CSharp.RuntimeBinder, do đó chúng sẽ không được hiển thị trong Intellisense. Không cần phải nói, điều này không có vẻ giống như một kịch bản được hỗ trợ, do đó, sử dụng nó có nguy cơ của riêng bạn!

Mã này gọi Bar phương pháp năng động mà không cần bất kỳ đối số trên một thể hiện của một lớp học có nguồn gốc từ DynamicObject:

dynamic dynamicObject = new DerivedFromDynamicObject(); 
var callSiteBinder = Binder.InvokeMember(CSharpBinderFlags.None, "Bar", Enumerable.Empty<Type>(), typeof(Program), 
    new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); 
var callSite = CallSite<Action<CallSite, object>>.Create(callSiteBinder); 
callSite.Target(callSite, dynamicObject); 

This bài đăng blog và this one có chi tiết đẫm máu hơn trên các trang web cuộc gọi và chất kết dính.

+0

Âm thanh vui nhộn. Tôi sẽ chờ đợi để xem một người nào đó thực sự có một giải pháp được hỗ trợ vì tôi có thể sẽ không được xung quanh nữa khi điều này ngừng hoạt động. – zneak

+1

Mặt khác, vì đó là những gì trình biên dịch đã làm, nó sẽ không bao giờ ngừng hoạt động, vì điều đó sẽ chỉ giết bất kỳ ứng dụng nào bằng cách sử dụng 'dynamic' được xây dựng ngày hôm nay. – zneak

+0

@zneak Đúng vậy, tôi nghĩ đó là một cược khá an toàn. Thật đáng ngạc nhiên khi họ đã đi đến độ dài lớn như vậy để đảm bảo các loại được ẩn. – Trillian

14

Tôi có một mã nguồn mở (Apache giấy phép) khung sườn Dynamitey (có sẵn trong nuget) mà đóng gói mã binder năng động, điều này bao gồm tự động bộ nhớ đệm các trang web cuộc gọi. Nó cũng có các phương thức tiện lợi cho mọi loại chất kết dính (getters, setters, events, indexers, operator, conversion), nhưng cụ thể là bạn muốn InvokeMember.

Mã binder động thực sự chạy nhanh hơn so với phản ánh (được phân bổ) khi gọi các thành viên của lớp được định nghĩa tĩnh (lúc biên dịch).

Dynamic.InvokeMember(foo,"Bar",arg...); 
+0

Trông khá tuyệt! – zneak

+0

Man! Nuget tuyệt vời! Hoạt động hoàn hảo! Cảm ơn bạn! nên được đánh dấu là câu trả lời! – CodeHacker

+0

Gói lớn, hoàn thành công việc. – Andro

Các vấn đề liên quan