2009-10-14 26 views
5

Tôi có một hàm nhận hai tham số - một đối tượng và một cấu trúc EventInfo xác định một sự kiện trên đối tượng đó. Tôi cần chặn chức năng đó cho đến khi sự kiện được chỉ định kích hoạt. Vấn đề tôi gặp phải là, làm cách nào để thêm đại biểu vào sự kiện được chỉ định, khi loại trình xử lý có thể là gì? Lưu ý rằng tôi không quan tâm đến các tham số của cuộc gọi kết quả đó, tôi chỉ cần nắm bắt thực tế nó được nâng lên.Một loại đại biểu chung để xử lý bất kỳ sự kiện nào

Tôi đã thử sử dụng EventInfo.AddEventHandler để thêm loại đại biểu thực sự chung (EventHandler), nhưng không có kết quả. Tôi cũng đã thử tương tự, nhưng sử dụng Activator để tạo một thể hiện của loại được chỉ định trong thuộc tính EventInfo.EventHandlerType, nhưng không có niềm vui.

Ngoài ra, nếu có ai đó có cách tương tự, đưa ra một đối tượng và tên của một sự kiện trên đối tượng đó, thì điều đó cũng sẽ hoạt động.

Tôi đang sử dụng C# và .NET 2.0.

Chúc mừng

Trả lời

2

Một gợi ý cho giải pháp có thể sử dụng lớp MethodBuilder. Sử dụng nó, bạn có thể tạo ra một phương thức tại thời gian chạy phù hợp với ủy nhiệm EventInfo mong đợi.

Ví dụ dựa vào nó (Nhiều tối ưu hóa có thể được thực hiện nhưng nó hoạt động cho hầu hết các trường hợp):

namespace AutoEventListener 
{ 
    using System; 
    using System.Linq; 
    using System.Collections.Generic; 
    using System.Reflection; 
    using System.Reflection.Emit; 

    public class EventExample 
    { 
     public static event EventHandler MyEvent; 

     public void Test() 
     { 
      bool called; 
      var eventInfo = GetType().GetEvent("MyEvent"); 
      EventFireNotifier.GenerateHandlerNorifier(eventInfo, 
       callbackEventInfo => 
        { 
         called = true; 
        }); 

      MyEvent(null, null);; 
     } 
    } 

    public class EventFireNotifier 
    { 
     static private readonly Dictionary<int, EventInfo> eventsMap = new Dictionary<int, EventInfo>(); 
     static private readonly Dictionary<int, Action<EventInfo>> actionsMap = new Dictionary<int, Action<EventInfo>>(); 
     static private int lastIndexUsed; 
     public static MethodInfo GenerateHandlerNorifier(EventInfo eventInfo, Action<EventInfo> action) 
     { 
      MethodInfo method = eventInfo.EventHandlerType.GetMethod("Invoke"); 
      AppDomain myDomain = AppDomain.CurrentDomain; 
      var asmName = new AssemblyName(){Name = "HandlersDynamicAssembly"}; 

      AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly(
       asmName, 
       AssemblyBuilderAccess.RunAndSave); 

      ModuleBuilder myModule = myAsmBuilder.DefineDynamicModule("DynamicHandlersModule"); 

      TypeBuilder typeBuilder = myModule.DefineType("EventHandlersContainer", TypeAttributes.Public); 

      var eventIndex = ++lastIndexUsed; 
      eventsMap.Add(eventIndex, eventInfo); 
      actionsMap.Add(eventIndex, action); 

      var handlerName = "HandlerNotifierMethod" + eventIndex; 

      var parameterTypes = method.GetParameters().Select(info => info.ParameterType).ToArray(); 
      AddMethodDynamically(typeBuilder, handlerName, parameterTypes, method.ReturnType, eventIndex); 

      Type type = typeBuilder.CreateType(); 

      MethodInfo notifier = type.GetMethod(handlerName); 

      var handlerDelegate = Delegate.CreateDelegate(eventInfo.EventHandlerType, notifier); 

      eventInfo.AddEventHandler(null, handlerDelegate); 
      return notifier; 
     } 

     public static void AddMethodDynamically(TypeBuilder myTypeBld, string mthdName, Type[] mthdParams, Type returnType, int eventIndex) 
     { 
      MethodBuilder myMthdBld = myTypeBld.DefineMethod(
               mthdName, 
               MethodAttributes.Public | 
               MethodAttributes.Static, 
               returnType, 
               mthdParams); 

      ILGenerator generator = myMthdBld.GetILGenerator(); 

      generator.Emit(OpCodes.Ldc_I4, eventIndex); 
      generator.EmitCall(OpCodes.Call, typeof(EventFireNotifier).GetMethod("Notifier"), null); 
      generator.Emit(OpCodes.Ret); 
     } 

     public static void Notifier(int eventIndex) 
     { 
      var eventInfo = eventsMap[eventIndex]; 
      actionsMap[eventIndex].DynamicInvoke(eventInfo); 
     } 
    } 
} 

Lớp EventFireNotifier đăng ký cho EventInfo một Action mà được gọi khi sự kiện này là Bị sa thải.

Tôi hy vọng điều đó sẽ hữu ích.

+0

Phương pháp này xuất hiện để tạo một dll cho mọi phương thức bạn tạo, có thể không làm chậm nó xuống một chút? – Kazar

+0

Tôi nghĩ rằng phần chậm là tạo ra các phương pháp (sử dụng phản ánh) nhưng một khi nó được biên dịch tôi nghĩ rằng nó cần phải có hiệu suất tốt (nhưng tôi không bao giờ làm nó vì vậy tôi không thể chắc chắn ...). – Elisha

+0

Điều đó trông giống như mã tốt (mặc dù nó đang sử dụng var, mà tôi không thể), tuy nhiên tôi đã tìm thấy cách giải quyết đơn giản hơn cho vấn đề của mình, bằng cách yêu cầu mã khởi tạo cấp cao nhất để cung cấp các đại biểu cho từng loại trình xử lý sự kiện. – Kazar

0

Tôi không hiểu đầy đủ câu hỏi, có thể ví dụ về mã có thể tốt hơn. Nhưng một người nghĩ rằng bạn có thể làm là thêm một đại biểu không tham số là người tổ chức sự kiện. Điều này sẽ làm việc bất kể loại đại biểu cho sự kiện được xác định.

someobject.someevent += delegate{ // do whatever;} 
+0

Chúc mừng cho đề xuất, nhưng tôi e rằng tôi không thực sự có quyền truy cập vào sự kiện, chỉ với cấu trúc EventInfo mô tả nó. – Kazar

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