2012-03-17 13 views
10

Considere:una acción para cualquier tipo de evento a través de la reflexión

someControl.Click += delegate { Foo(); }; 

Los argumentos del evento son irrelevantes, no necesito de ellos y no estoy interesado en ellos. Solo quiero que llamen a Foo(). No hay una manera obvia de hacer lo mismo a través de la reflexión.

me gustaría traducir lo anterior en algo en la línea de

void Foo() { /* launch missiles etc */ } 

void Bar(object obj, EventInfo info) 
{ 
    Action callFoo = Foo; 
    info.AddEventHandler(obj, callFoo); 
} 

Además, yo no quiero hacer la hipótesis de que el tipo de objeto que se pasa a Bar se adhiere estrictamente a las directrices de utilizando la firma EventHander (TArgs) para eventos. Para decirlo simplemente, estoy buscando una forma de suscribir una Acción a cualquier tipo de controlador; menos simplemente, una forma de convertir el delegado de Acción en un delegado del tipo de controlador esperado.

+4

+1 Para el lanzamiento de misiles! – Jason

+1

¿Qué esperaría que ocurriera si el tipo de delegado utilizado para el evento tuviera un tipo de devolución no nulo o un parámetro 'out'? –

+0

@JonSkeet Buen punto, realmente no lo consideré; los parámetros de salida y los valores de retorno no son realmente útiles para los eventos. Pero esperaría un error de conversión, una excepción, etc. Básicamente, me gustaría suponer que esos tipos no se usarían como controladores de eventos para mi propia cordura. – Siege

Respuesta

7
static void AddEventHandler(EventInfo eventInfo, object item, Action action) 
{ 
    var parameters = eventInfo.EventHandlerType 
    .GetMethod("Invoke") 
    .GetParameters() 
    .Select(parameter => Expression.Parameter(parameter.ParameterType)) 
    .ToArray(); 

    var handler = Expression.Lambda(
     eventInfo.EventHandlerType, 
     Expression.Call(Expression.Constant(action), "Invoke", Type.EmptyTypes), 
     parameters 
    ) 
    .Compile(); 

    eventInfo.AddEventHandler(item, handler); 
} 
static void AddEventHandler(EventInfo eventInfo, object item, Action<object, EventArgs> action) 
{ 
    var parameters = eventInfo.EventHandlerType 
    .GetMethod("Invoke") 
    .GetParameters() 
    .Select(parameter => Expression.Parameter(parameter.ParameterType)) 
    .ToArray(); 

    var invoke = action.GetType().GetMethod("Invoke"); 

    var handler = Expression.Lambda(
     eventInfo.EventHandlerType, 
     Expression.Call(Expression.Constant(action), invoke, parameters[0], parameters[1]), 
     parameters 
    ) 
    .Compile(); 

    eventInfo.AddEventHandler(item, handler); 
} 

Uso:

Action action =() => BM_21_Grad.LaunchMissle(); 

    foreach (var eventInfo in form.GetType().GetEvents()) 
    { 
    AddEventHandler(eventInfo, form, action); 
    } 
+0

¡Ah, confuso, pero funciona! Y me hizo darme cuenta de la genialidad de las expresiones (nunca tuve una razón para usarlas). No lo he probado a fondo, pero creo que tengo lo que necesito. Gracias;) – Siege

+0

Estuve buscando dos días :) Gracias, muchas gracias :))) –

+0

Pero tengo una pregunta. Cuando cambié Action to Action = (o, e) => ..., Su error ... ¿Me pueden ayudar con esta pregunta? –

0

¿Qué tal esto?

void Bar(object obj, EventInfo info) 
{ 
    var parameters = info.EventHandlerType.GetMethod("Invoke").GetParameters() 
     .Select(p => Expression.Parameter(p.ParameterType)); 

    var handler = Expression.Lambda(
     info.EventHandlerType, 
     Expression.Call(
      Expression.Constant(obj), // obj is the instance on which Foo() 
      "Foo",     // will be called 
      null 
     ), 
     parameters 
    ); 
    info.AddEventHandler(obj, handler.Compile()); 
} 
Cuestiones relacionadas