Una pista para la solución se puede hacer con la clase MethodBuilder. Utilizándolo, puede generar un método en tiempo de ejecución que se ajuste al delegado que espera EventInfo.
Ejemplo basado en él (Muchas optimizaciones se puede hacer, pero funciona para la mayoría de los casos):
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);
}
}
}
La clase EventFireNotifier registro de EventInfo un Acción que se llama cuando el evento es despedido.
Espero que ayude.
El método parece crear un archivo DLL para cada método que crees, ¿no podría eso ralentizarlo un poco? – Kazar
Creo que la parte lenta es crear el método (usa reflexión) pero una vez compilado creo que debería tener un buen rendimiento (pero nunca lo hice, así que no puedo asegurarlo ...). – Elisha
Parece un buen código (aunque utiliza var, lo que no puedo), sin embargo, encontré una solución más simple para mi problema, requiriendo que el código de inicialización de nivel superior proporcione delegados para cada tipo de controlador de eventos. – Kazar