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.