2012-01-04 15 views
13

tengo la línea siguiente en C#:¿Cómo invocar manualmente un evento?

_timer.ElapsedTick += _somefunction1; 
_timer.ElapsedTick += _somefunction2; 
_timer.ElapsedTick += _somefunction3; 

cómo invocar todos los métodos suscritos a _timer.ElapsedTick sin especificar el _somefunction? En algún lugar a lo largo de esta pseudo-línea

invoke(_timer.ElapsedTick); 

Su ayuda es muy apreciada.

actualización

Ejemplo de @ M.Babcock está trabajando. El método FireEvent hace la magia. Gracias.

class InvokeFromMe 
{ 
    public event EventHandler RaiseMe; 
} 
class MainClass 
{ 
    public MainClass() 
    { 
     InvokeFromMe fromMe = new InvokeFromMe(); 
     fromMe.RaiseMe += fromMe_RaiseMe; 
     fromMe.RaiseMe += fromMe_RaiseMe1; 
     FireEvent(fromMe, "RaiseMe", null, EventArgs.Empty); 
     //works 

     System.Timers.Timer _timer = new System.Timers.Timer(); 
     _timer.Elapsed += _timer_Elapsed; 
     FireEvent(_timer, "onIntervalElapsed", null, null); 
     //throws exception 
    } 
    private void _timer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     Console.WriteLine("_timer_Elapsed raised"); 
    } 
    private void fromMe_RaiseMe(object sender, EventArgs e) 
    { 
     Console.WriteLine("Event Handler 0 Raised"); 
    } 
    private void fromMe_RaiseMe1(object sender, EventArgs e) 
    { 
     Console.WriteLine("Event Handler 1 Raised"); 
    } 
    public void FireEvent(object onMe, string invokeMe, params object[] eventParams) 
    { 
     MulticastDelegate eventDelagate = 
       (MulticastDelegate)onMe.GetType().GetField(invokeMe, 
       System.Reflection.BindingFlags.Instance | 
       System.Reflection.BindingFlags.NonPublic).GetValue(onMe); 

     Delegate[] delegates = eventDelagate.GetInvocationList(); 

     foreach (Delegate dlg in delegates) 
     { 
      dlg.Method.Invoke(dlg.Target, eventParams); 
     } 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     MainClass m = new MainClass(); 
    } 
} 

Respuesta

17

¿Se puede hacer utilizando el C# convencional? No (as previously stated). Pero usando la reflexión es posible. Aquí hay un código probado basado en la respuesta a this MSDN forum thread:

class InvokeFromMe 
{ 
    public event EventHandler RaiseMe; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var fromMe = new InvokeFromMe(); 

     fromMe.RaiseMe += fromMe_RaiseMe; 
     fromMe.RaiseMe += fromMe_RaiseMe1; 
     fromMe.RaiseMe += fromMe_RaiseMe2; 

     FireEvent(fromMe, "RaiseMe", null, EventArgs.Empty); 
    } 

    static void fromMe_RaiseMe(object sender, EventArgs e) 
    { 
     Console.WriteLine("Event Handler 0 Raised"); 
    } 
    static void fromMe_RaiseMe1(object sender, EventArgs e) 
    { 
     Console.WriteLine("Event Handler 1 Raised"); 
    } 
    static void fromMe_RaiseMe2(object sender, EventArgs e) 
    { 
     Console.WriteLine("Event Handler 2 Raised"); 
    } 

    public static void FireEvent(object onMe, string invokeMe, params object[] eventParams) 
    { 
     MulticastDelegate eventDelagate = 
       (MulticastDelegate)onMe.GetType().GetField(invokeMe, 
       System.Reflection.BindingFlags.Instance | 
       System.Reflection.BindingFlags.NonPublic).GetValue(onMe); 

     Delegate[] delegates = eventDelagate.GetInvocationList(); 

     foreach (Delegate dlg in delegates) 
     { 
      dlg.Method.Invoke(dlg.Target, eventParams); 
     } 
    } 

} 

ACTUALIZACIÓN

No estoy familiarizado con la clase System.Timer.Timer, así que no estoy seguro de lo que es diferente de mi ejemplo proporcionado. Usted tal vez podría intentar algo como:

public static void FirePublicEvent(object onMe, string invokeMe, params object[] eventParams) 
{ 
    MulticastDelegate eventDelagate = 
      (MulticastDelegate)onMe.GetType().GetField(invokeMe, 
      System.Reflection.BindingFlags.Instance | 
      System.Reflection.BindingFlags.Public).GetValue(onMe); 

    Delegate[] delegates = eventDelagate.GetInvocationList(); 

    foreach (Delegate dlg in delegates) 
    { 
     dlg.Method.Invoke(dlg.Target, eventParams); 
    } 
} 
+0

¡El FireEvent es exactamente lo que estoy buscando, casi! Actualmente me está dando un FieldInfo = null en el GetField. ¿Me estoy perdiendo un BindingFlag? El evento de destino es System.Timer.Timer.ElapsedTick. Los métodos de alguna función son actualmente privados, pero puedo cambiarlos a internos o públicos. –

+0

@JesonMartajaya - ¿Quiere decir 'System.Timers.Timer.Elapsed'? Esto no funcionará si el evento es privado. Sin embargo, los controladores de eventos pueden ser privados. –

+0

@JesonMartajaya - ¿Puedo ver su llamada a 'FireEvent'? Siempre que 'onMe' sea una instancia de' System.Timer.Timer', entonces debería pasar 'ElapsedTick' para 'invokeMe'. También actualizaré mi respuesta para cubrir el caso donde el evento es 'público'. –

1

Se puede crear una función que les corre todo:

public void RunAllFuncs() 
{ 
    _somefunction1(); 
    _somefunction2(); 
    _somefunction3(); 
} 
+0

Definitivamente no es lo que estoy buscando. No tengo ninguna referencia a los métodos de función aparte de ver _timer.ElapsedTick. –

-1

Dependiendo de la firma del evento ElapsedTick, puede desencadenar el evento llamando como un método de dentro de la clase en la cual es declarado. Por ejemplo:

ElapsedTick(this, new EventArgs()); 
+1

'EventArgs.Empty' es bueno ya que evita una asignación innecesaria. Además, no puede invocar un evento desde fuera de la clase de esa manera. –

+0

No mencioné que está activado de esta manera desde el interior de la clase. Además, usar 'EventArgs.Empty' es un buen toque. – Bernard

+1

Intenté lo siguiente _timer.ElapsedTick (this, ElapsedEventArgs.Empty); y proporciona un error en tiempo de compilación: 'System.Timers.Timer' no contiene una definición para 'ElapsedTick' y no es posible aplicar ningún método de extensión 'ElapsedTick' que acepte un primer argumento de tipo 'System.Timers.Timer'. encontrado (¿te falta una directiva de uso o una referencia de ensamblado?) –

-1

Solo puede llamar eventos desde dentro de la clase que lo declara. Si escribió la clase temporizador, debe invocar esta manera:

this.ElapsedTick(this, eventArgs); 

Fuera de la clase temporizador puede hacerlo de la siguiente manera para conseguir un comportamiento similar:

delegate void delegateSample(object sender, EventArgs e); 

delegateSample d; 
d += new delegateSample(_somefunction1); 
d += new delegateSample(_somefunction2); 
d(this, EventArgs.Empty); 

delegateSample deberían ser en realidad el tipo de la Evento ElapsedTick, en lugar de declarar otro tipo de delegado. Simplemente no sabía el nombre de ese delegado. Simplemente asegúrese de agregar cada función al evento ElapsedTick y la variable de delegado que declara, luego puede llamarlos a todos desde el delegado que está fuera de la clase Timer.

+0

En muchos casos, es posible llamar eventos a través de la barrera de clase. El truco es hacerlo a través de ensamblajes. – ouflak

16

No puede invocar un evento que pertenece a otro tipo. Un evento solo puede invocarse desde el interior de la clase que lo declara.

+0

@ JaredPar- Respetuosamente, no estoy de acuerdo. El ejemplo que publiqué a continuación funciona bien para mí en todas las clases. A menos que, por supuesto, estemos hablando de * hilos * en lugar de clases. Y, incluso entonces, puede funcionar con alguna codificación adicional. –

+5

@MatthewPatrickCashatt sí pero estás usando el reflejo. Esto se puede usar para evitar una gran cantidad de restricciones tanto en CLR como en C#. En general, no considero que el reflejo sea una respuesta válida para "cómo puedo hacer X" Definitivamente funciona – JaredPar

+0

Gracias JaredPar. Para mi propia educación, ¿por qué mi respuesta sería inválida si definitivamente funciona? –

1

Creo que es probable que necesite usar InvokeMember:

public void GetMethod(string methodName){ 

      var args = new Object[] { [INSERT ARGS IF NECESSARY--SET TO NULL OTHERWISE] }; 
      try 
      { 
       var t = new [INSERT NAME OF CLASS THAT CONTAINS YOUR METHOD](); 
       Type typeInfo = t.GetType(); 
       var result = typeInfo.InvokeMember(methodName, BindingFlags.InvokeMethod, null, t, args); 
       return result; 
      } 
      catch (Exception ex) 
      { 
       return ex; 
      } 
} 

Entonces lo llaman así:

_timer.ElapsedTick += GetMethod("[INSERT METHOD NAME AS STRING]"); 

Asegúrese de incluir lo siguiente:

using System.Reflection; 

buena suerte!

+0

Para hacerlo más claro, estoy buscando levantar manualmente el evento _timer.ElapsedTick. De su solución propuesta, ¿cree que puedo llamar a GetMethod ("_ timer.ElapsedTick")? –

+0

Esto está cerca pero no del todo. Su código aquí ejecuta un método directamente. Primero debe obtener el campo que representa el evento en el tipo como 'MulticastDelegate', luego obtener su invocationlist y desde allí puede invocar dinámicamente cada método individualmente. La respuesta a [esto] (http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/44b0d573-5c53-47b0-8e85-6056cbae95b0) La pregunta de MSDN muestra cómo hacerlo. –

+0

Puede llamar a cualquier * método * que quiera utilizando "GetMethod", pero solo eche un vistazo a los comentarios de @ JaredPar ya que tiene algunas reservas. He estado usando GetMethod con éxito, pero es difícil ignorar a un tipo con reputación 178k cuando advierte en contra. ¡Buena suerte! –

2

Esto es una solución alternativa, no puede recorrer el evento transcurrido. Pero para invocarlos a todos, deje que el temporizador haga el trabajo. Este código para System.Timer, no está seguro de qué temporizador estás usando.

Suponiendo que el temporizador ya está activado:

int interval = timer.Interval; 
ElapsedEventHandler handler = null; 
handler = (s,e) => 
{ 
    timer.Interval = interval; // put interval back to original value 
    timer.Elapsed -= handler; 
}; 
timer.Elapsed += handler; 
timer.Interval = 1; // 1 millisecond, pretty much going to fire right now (as soon as you let it) 

Algo así como que se disparará los eventos, pero se reiniciará el intervalo inicial. Puede que tenga que hacer algunos cálculos allí si desea mantener el patrón de marcación original.

0

Basado en M.Babcock's answer anterior, con algunos cambios leves método de reflexión.

Reemplazado con GetFieldGetTypeInfo y GetDeclaredField y dlg.Method con dlg.GetMethodInfo.

using System.Reflection; 

    ... 

    public static void FireEvent(this object onMe, string invokeMe, params object[] eventParams) 
    { 
     TypeInfo typeInfo = onMe.GetType().GetTypeInfo(); 
     FieldInfo fieldInfo = typeInfo.GetDeclaredField(invokeMe); 
     MulticastDelegate eventDelagate = (MulticastDelegate)fieldInfo.GetValue(onMe); 

     Delegate[] delegates = eventDelagate.GetInvocationList(); 

     foreach (Delegate dlg in delegates) 
     { 
      dlg.GetMethodInfo().Invoke(dlg.Target, eventParams); 
     } 
    } 
Cuestiones relacionadas