2012-03-17 9 views
10

Considerare:La sottoscrizione di un'azione a qualsiasi tipo di evento tramite riflessione

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

Gli argomenti della manifestazione sono irrilevanti, non ho bisogno di loro e io non sono interessato a loro. Voglio solo Foo() per essere chiamato. Non c'è un modo ovvio per fare lo stesso tramite la riflessione.

mi piacerebbe tradurre il sopra in qualcosa sulla falsariga di

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

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

Inoltre, io non voglio fare l'ipotesi che il tipo di oggetto passato alla Bar rigorosamente aderisce alle linee guida della utilizzando la firma EventHander (TArgs) per gli eventi. Per dirla semplicemente, sto cercando un modo per iscrivere un'azione a qualsiasi tipo di gestore; meno semplicemente, un modo per convertire il delegato dell'Azione in un delegato del tipo di gestore previsto.

+4

+1 Per lanciare i missili! – Jason

+1

Cosa ti aspetti che succeda se il tipo di delegato utilizzato per l'evento avesse un tipo di reso non vuoto, o un parametro 'out'? –

+0

@JonSkeet Buon punto, non l'ho davvero preso in considerazione; i parametri out e i valori restituiti non sono realmente utili per gli eventi. Ma mi aspetterei un errore di conversione, un'eccezione, ecc. Fondamentalmente mi piacerebbe presumere che quei tipi non sarebbero usati come gestori di eventi per la mia sanità mentale. – Siege

risposta

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

Usage:

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

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

Ah, confuso, ma funziona! E mi ha fatto capire la bellezza delle espressioni (non ho mai avuto un motivo per usarle). Non l'ho provato a fondo, ma penso di avere ciò di cui ho bisogno. Grazie;) – Siege

+0

Ero alla ricerca di due giorni :) Grazie, grazie mille :))) –

+0

Ma ho una domanda. Quando ho cambiato Azione in azione = (o, e) => ..., Il suo errore .. Puoi aiutarmi a questa domanda? –

0

ne dici di questo?

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()); 
} 
Problemi correlati