【问题标题】:Attaching an event handler to a generic type initialized using reflection at runtime with unknown type parameter将事件处理程序附加到使用未知类型参数在运行时使用反射初始化的泛型类型
【发布时间】:2013-01-29 10:43:54
【问题描述】:

请看下面的代码,这是基于你有一个控制器类 Controller 的假设。它是一个具有约束 CGeneric 的泛型类,其中 T:IRecord、两个具体的记录类 CRecordCustomer:IRecord 和 CRecordVipCustomer:Irecord。问题是如何在运行前不知道 t 类型的情况下将事件处理程序附加到泛型类型?

public class CGeneric<T> where T:IRecord, new()
{
public delegate void OnCompleted();
public event OnCompleted completed;

private void ProcessStuff(T ConcreteRecordType)
{
    T concreteInstance = default(T);
    (concreteInstance as T).DoSomeInterfaceStuff();
    if(this.completed !=null)
    {
        this.completed;
    }
}
}

// This is how the controller class instantiates CGeneric<T> 
// Using reflection gets all types that implement IRecord
// Then using one of those types (which is unknown at compile time):

class Controller
{

Type[] allTypes = Assembly.GetExecutingAssembly().GetTypes();

    Type concreteType allTypes.Where(t => t.GetInterfaces().Contains(typeof(IRecord)) &&      !IgnoreType(t)).ToList()[0];


    Type genericType = typeof(CGeneric<>);

    genericType = genericType .MakeGenericType(
    ConstructorInfo constructor = genericType .GetConstructor(new Type[] { });
    Object genericInstance = constructor.Invoke(new Object[] { });

//This is where I need to hook to OnCompletedEvent

    MethodInfo processmoethod = genericType .GetMethod("Process");

    processmoethod.Invoke(genericInstance , concreteType );
}

【问题讨论】:

  • 你试过编译这段代码吗?

标签: c# reflection delegates event-handling generic-programming


【解决方案1】:

通常,您应该能够按如下方式添加事件处理程序:

OnCompleted handler = () => { /*event handler*/ };
genericType.GetEvent("completed").AddEventHandler(genericInstance, handler);

但是,您应该将 OnCompleted 委托定义移到类之外,以便能够在不知道 CGeneric&lt;T&gt;T 的情况下引用它。

(注意:您的示例代码有很多其他错误会阻止您编译它)

【讨论】:

    【解决方案2】:

    【讨论】:

      【解决方案3】:

      解决办法可能是这样的:

      public delegate void OnCompleted();
      
      public interface IRecord
      {
          void DoSomeInterfaceStuff();
          event OnCompleted completed;
      }
      
      public interface IEvents
      {
          event OnCompleted completed;
      }
      
      public class CGeneric<T> : IEvents
          where T:IRecord, new()
      {
          public event OnCompleted completed;
          private void ProcessStuff(T ConcreteRecordType)
          {
              T concreteInstance = default(T);
      
              concreteInstance.DoSomeInterfaceStuff();
      
              if(this.completed !=null)
              {
                  this.completed();
              }
          }
      }
      
      public class Record : IRecord
      {
          public void DoSomeInterfaceStuff()
          {
      
          }
      }
      

      并且可以这样实例化:

          Type toInstantiate = Type.GetType("CGeneric`1");
          Type[] parameters = new Type[] { typeof(Record) };
          Type instantiated = toInstantiate.MakeGenericType(parameters);
      
          IEvents result = Activator.CreateInstance(instantiated) as IEvents;
          result.completed += result_completed;
      

      而且它不依赖于泛型类型。

      【讨论】:

        【解决方案4】:

        Lats 说你有 eventNamehandlerMethodNameobjectOnWhichTheEventIsDefinedobjectOnWhichTheEventHandlerIsDefinedeventNamehandlerMethodName 是字符串,其余的对象是对象数据类型。然后,您可以使用反射将事件与处理程序连接起来,如下所示。

        public bool SubscribeEvent(string eventName, string handlerMethodName, 
            object objectOnWhichTheEventIsDefined, 
            object objectOnWhichTheEventHandlerIsDefined)
        {
            try
            {
                var eventInfo = objectOnWhichTheEventIsDefined.GetType().GetEvent(eventName);
                var methodInfo = objectOnWhichTheEventHandlerIsDefined.GetType().
                GetMethod(handlerMethodName);
        
                // Create new delegate mapping event to handler
                Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, objectOnWhichTheEventHandlerIsDefined, methodInfo);
                eventInfo.AddEventHandler(objectOnWhichTheEventIsDefined, handler);
                return true;
            }
            catch (Exception ex)
            {
                // Log failure!
                var message = "Exception while subscribing to handler. Event:" + eventName + " - Handler: " + handlerMethodName + "- Exception: " + ex;
                Debug.Print(message);
                return false;
            }
        }
        

        完整的控制台示例如下所示。

        class Program
        {
            static void Main(string[] args)
            {
                var typeWithEvent = new TypeWithEvent();
                var typeWithEventHandler = new TypeWithEventHandler();
        
                SubscribeEvent("EventTest", "EventHandlerMethod", typeWithEvent, typeWithEventHandler);
        
                EventArgs e = new EventArgs();
                Console.WriteLine("Event is about to be raised.");
                typeWithEvent.OnTimeToRaiseTheEvent(e);
                Console.WriteLine("Event trigger is completed.");
                Console.ReadLine();
            }
            static bool SubscribeEvent(string eventName, string handlerMethodName, object objectOnWhichTheEventIsDefined, object objectOnWhichTheEventHandlerIsDefined)
            {
                try
                {
                    var eventInfo = objectOnWhichTheEventIsDefined.GetType().GetEvent(eventName);
                    var methodInfo = objectOnWhichTheEventHandlerIsDefined.GetType().
                    GetMethod(handlerMethodName);
        
                    // Create new delegate mapping event to handler
                    Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, objectOnWhichTheEventHandlerIsDefined, methodInfo);
                    eventInfo.AddEventHandler(objectOnWhichTheEventIsDefined, handler);
                    return true;
                }
                catch (Exception ex)
                {
                    // Log failure!
                    var message = "Exception while subscribing to handler. Event:" + eventName + " - Handler: " + handlerMethodName + "- Exception: " + ex;
                    Debug.Print(message);
                    return false;
                }
            }
        }
        internal class TypeWithEvent
        {
            public event EventHandler<EventArgs> EventTest;
            internal void OnTimeToRaiseTheEvent(EventArgs e)
            {
                // Thread safe way to raise event as described in Events chapter 11 of
                // the book CLR Via C#, 4th Edition, by Jeffrey Richter
                EventHandler<EventArgs> temp = Volatile.Read(ref EventTest);
                if (temp != null) temp(this, e);
            }
        }
        
        internal class TypeWithEventHandler
        {
            public void EventHandlerMethod(Object sender, EventArgs e)
            {
                Console.WriteLine("From the event handler method.");
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2019-03-16
          • 2017-03-24
          • 2018-12-15
          • 1970-01-01
          • 2017-02-21
          • 1970-01-01
          • 1970-01-01
          • 2023-02-23
          • 1970-01-01
          相关资源
          最近更新 更多