简体   繁体   中英

Attaching an event handler to a generic type initialized using reflection at runtime with unknown type parameter

Please have a look at code below, which is based on the assumption that you have a controller class Controller. It is a generic class with constraint CGeneric where T:IRecord, two concrete record classes CRecordCustomer:IRecord, and CRecordVipCustomer:Irecord. The question is how to attach event handler to a generic type without knowing type of t before runtime?

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 );
}

Normally, you should be able to add an event handler as follows:

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

However, you should move your OnCompleted delegate definition outside the class in order to be able to reference it without knowing the T of CGeneric<T> .

(Note: your sample code has a lot of other errors that will prevent you from compiling it)

A solution may be this:

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()
    {

    }
}

And could be instantiated in this way:

    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;

And it's not dependent on the generic type.

Lats say you have the eventName , handlerMethodName , objectOnWhichTheEventIsDefined , objectOnWhichTheEventHandlerIsDefined . The eventName and handlerMethodName are strings and the rest of the objects are of object data type. Then you can wire up the event with a handler using reflection as follows.

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;
    }
}

A full console example would look like this.

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.");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM