2010-12-07 12 views
20

tengo una clase, EventContainer.cs, que contiene un evento, por ejemplo:provocar un evento de una clase de una clase diferente en C#

public event EventHandler AfterSearch; 

tengo otra clase, EventRaiser.cs. ¿Cómo elevo (y no manejo) el evento arriba mencionado de esta clase?

El evento elevado a su vez llamará al controlador del evento en la clase EventContainer. Algo como esto (esto obviamente no es correcto):

EventContainer obj = new EventContainer(); 
RaiseEvent(obj.AfterSearch); 
+0

Este antiguo puesto plantea un problema que puede evitarse fácilmente utilizando un concentrador evento como [TinyMessenger ] (https://github.com/grumpydev/TinyMessenger). – Larry

Respuesta

29

Esto no es posible, los eventos solo pueden surgir desde el interior de la clase. Si pudieras hacer eso, vencería el propósito de los eventos (pudiendo subir los cambios de estado desde el interior de la clase). Creo que está malinterpretando la función de los eventos: un evento se define dentro de una clase y otros pueden suscribirse haciendo

obj.AfterSearch += handler; (donde el controlador es un método de acuerdo con la firma AfterSearch). Uno puede suscribirse al evento desde el exterior, pero solo puede surgir desde el interior de la clase que lo define.

+0

Gracias Femaref por la información privilegiada. Trataremos de lograr de alguna otra manera. – samar

+3

@Femaref: ** POSIBLE **, prueba mi respuesta a continuación. – halorty

+0

¿Tiene practicidad? No, no es así, tu primera línea lo dice, es un truco, si lo encontrara en algún lugar del código fuente, cerraría lentamente la ventana y trabajaría con calma, para no volver a ser visto nunca más. – Femaref

5

Puede escribir un método público en la clase que desea que el evento active y activar el evento cuando se llame. A continuación, puede llamar a este método desde cualquier usuario de su clase.

Por supuesto, esto arruina la encapsulación y es un mal diseño.

2

De acuerdo con Femaref, y tenga en cuenta que esta es una diferencia importante entre delegados y eventos (consulte, por ejemplo, this blog entry para obtener una buena discusión sobre esta y otras diferencias).

Dependiendo de lo que desee lograr, es posible que esté mejor con un delegado.

6

Parece que está usando el Delegate pattern. En este caso, el evento AfterSearch debe definirse en la clase EventRaiser, y la clase EventContainer debe consumir el evento:

en EventRaiser.cs

public event EventHandler BeforeSearch; 
public event EventHandler AfterSearch; 

public void ExecuteSearch(...) 
{ 
    if (this.BeforeSearch != null) 
     this.BeforeSearch(); 

    // Do search 

    if (this.AfterSearch != null) 
     this.AfterSearch(); 
} 

en EventContainer.cs

public EventContainer(...) 
{ 
    EventRaiser er = new EventRaiser(); 

    er.AfterSearch += this.OnAfterSearch; 
} 

public void OnAfterSearch() 
{ 
    // Handle AfterSearch event 
} 
3

Hay una buena manera de hacerlo. Cada evento en C# tiene un delegado que especifica el signo de los métodos para ese evento. Defina un campo en su clase externa con el tipo de delegado de su evento. obtenga la referencia de ese campo en el constructor de clase externa y guárdelo. En la clase principal de su evento, envíe la referencia del evento para el delegado de la clase externa. Ahora puede llamar fácilmente al delegado en su clase externa.

public delegate void MyEventHandler(object Sender, EventArgs Args); 

public class MyMain 
{ 
    public event MyEventHandler MyEvent; 
    ... 
    new MyExternal(this.MyEvent); 
    ... 
} 

public MyExternal 
{ 
    private MyEventHandler MyEvent; 
    public MyExternal(MyEventHandler MyEvent) 
    { 
      this.MyEvent = MyEvent; 
    } 
    ... 
    this.MyEvent(..., ...); 
    ... 
} 
+0

No puedo aclarar ni responder esta respuesta; el ... deja de funcionar y se esconde demasiado, la reutilización del nombre de la variable MyEvent no ayuda y no creo que resuelva el problema. –

+0

@webturner: Está declarando un evento en una clase, luego pasándolo a otra clase durante la creación de instancias y llamando al evento desde la nueva clase. Es hackish, seguro, pero creo que funcionaría. – RobinHood70

+1

Esto no funcionaría. El manejador de eventos se copia y todos los suscriptores que se subscribieron después de la copia no serían notificados. – Johannes

14

es posible, pero utilizando artilugio ingenioso.

Inspirado por http://netpl.blogspot.com/2010/10/is-net-type-safe.html

Si usted no cree, pruebe este código.

using System; 
using System.Runtime.InteropServices; 

namespace Overlapping 
{ 
    [StructLayout(LayoutKind.Explicit)] 
    public class OverlapEvents 
    { 
     [FieldOffset(0)] 
     public Foo Source; 

     [FieldOffset(0)] 
     public OtherFoo Target; 
    } 

    public class Foo 
    { 
     public event EventHandler Clicked; 

     public override string ToString() 
     { 
      return "Hello Foo"; 
     } 

     public void Click() 
     { 
      InvokeClicked(EventArgs.Empty); 
     } 

     private void InvokeClicked(EventArgs e) 
     { 
      var handler = Clicked; 
      if (handler != null) 
       handler(this, e); 
     } 
    } 

    public class OtherFoo 
    { 
     public event EventHandler Clicked; 

     public override string ToString() 
     { 
      return "Hello OtherFoo"; 
     } 

     public void Click2() 
     { 
      InvokeClicked(EventArgs.Empty); 
     } 

     private void InvokeClicked(EventArgs e) 
     { 
      var handler = Clicked; 
      if (handler != null) 
       handler(this, e); 
     } 

     public void Clean() 
     { 
      Clicked = null; 
     } 
    } 

    class Test 
    { 
     public static void Test3() 
     { 
      var a = new Foo(); 
      a.Clicked += AClicked; 
      a.Click(); 
      var o = new OverlapEvents { Source = a }; 
      o.Target.Click2(); 
      o.Target.Clean(); 

      o.Target.Click2(); 
      a.Click(); 
     } 

     static void AClicked(object sender, EventArgs e) 
     { 
      Console.WriteLine(sender.ToString()); 
     } 
    } 
} 
+0

¡Esa es la cosa más genial que he visto en mucho tiempo! –

+0

¡Realmente increíble!Nunca vi "esto" en una clase de otro tipo que el tipo de la clase en la que se usa. –

0

Tuve una confusión similar y honestamente encuentro las respuestas aquí para ser confuso. Aunque un par insinuó soluciones que luego encontraría que funcionarían.

Mi solución fue llegar a los libros y familiarizarme con los delegados y los controladores de eventos. Aunque he usado ambos durante muchos años, nunca estuve íntimamente familiarizado con ellos. http://www.codeproject.com/Articles/20550/C-Event-Implementation-Fundamentals-Best-Practices da la mejor explicación de los delegados y controladores de eventos que he leído y explica claramente que una clase puede ser un editor de eventos y tener otras clases que los consumen. En este artículo: http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single se describe cómo lanzar eventos solo a un controlador, ya que los delegados son multidifusión por definición. Un delegado hereda el sistema. MulticastDelegate más, incluidos los delegados del sistema, son Multidifusión. Descubrí que multidifusión significaba que cualquier controlador de eventos con la misma firma recibiría el evento planteado. El comportamiento de multidifusión me ha causado algunas noches de insomnio mientras revisaba el código y veía mi evento aparentemente erróneamente enviado a los controladores que no tenía intención de conseguir este evento. Ambos artículos explican este comportamiento. El segundo artículo muestra una forma, y ​​el primer artículo muestra otra, haciendo que el delegado y la firma estén bien escritos. Personalmente creo que la tipificación fuerte evita errores estúpidos que pueden ser difíciles de encontrar. Así que votaría por el primer artículo, aunque obtuve el código del segundo artículo funcionando. Solo tenía curiosidad. :-)

También me llamó la atención si podía obtener el código de artículos # 2 para comportarme de la misma forma en que interpreté la pregunta original anterior. Independientemente de su enfoque elegido o si también estoy malinterpretando la pregunta original, mi verdadero mensaje es que todavía creo que se beneficiaría al leer el primer artículo como lo hice, especialmente si las preguntas o respuestas en esta página lo dejan confundido. Si tiene pesadillas de multidifusión y necesita una solución rápida, entonces el artículo 2 puede ayudarlo.

Empecé a jugar con la clase eventRaiser del segundo artículo. Hice un proyecto de formulario de Windows simple. Agregué la segunda clase de artículos EventRaiser.cs a mi proyecto. En el código del formulario principal, que define una referencia a esa clase EventRaiser en la parte superior como

private EventRaiser eventRaiser = new EventRaiser(); 

añadí un método en el código principal forma que quería ser llamado cuando el evento fue despedido

protected void MainResponse(object sender, EventArgs eArgs) 
{    
    MessageBox.Show("got to MainResponse"); 
} 

continuación, en el constructor de la forma principal por la que añade la asignación de eventos:

eventRaiser.OnRaiseEvent += new EventHandler(MainResponse);` 

luego, creamos una clase que se crea una instancia por mi forma principal se llama "Si mpleClass "por falta de ingenio creativo en este momento.

Luego añade un botón y en el evento de clic del botón que la instancia del código SimpleClass quería provocar un evento de:

private void button1_Click(object sender, EventArgs e) 
    {    
     SimpleClass sc = new SimpleClass(eventRaiser); 
    } 

Nota la instancia de "eventRaiser" que pasé a SimpleClass.cs . Eso fue definido e instanciado anteriormente en el código de formulario principal.

En los SimpleClass:

using System.Windows.Forms; 
using SinglecastEvent; // see SingleCastEvent Project for info or http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single 

    namespace GenericTest 
    { 

     public class SimpleClass 
     { 

      private EventRaiser eventRaiser = new EventRaiser(); 

      public SimpleClass(EventRaiser ev) 
      { 
       eventRaiser = ev; 
       simpleMethod(); 

      } 
      private void simpleMethod() 
      { 

       MessageBox.Show("in FileWatcher.simple() about to raise the event"); 
       eventRaiser.RaiseEvent(); 
      } 
     } 
    } 

El único punto en el método privado llamé SimpleMethod fue verificar que un método de ámbito privado todavía podría provocar el evento, no es que lo dudaba, pero me gusta estar positivo.

Ejecuté el proyecto y esto dio como resultado el aumento del evento del "método simple" de "SimpleClass" hasta el formulario principal y el método correcto esperado llamado MainResponse que demuestra que una clase puede generar un evento que es consumido por una clase diferente. Sí, el evento tiene que plantearse desde dentro de la clase que necesita su cambio de transmisión a otras clases que se preocupan. Las clases de recepción pueden ser de una clase o de muchas clases, dependiendo de qué tan fuertemente las haya tipeado o si las ha lanzado como en el segundo artículo.

Espero que esto ayude y no enturbie el agua. Personalmente, tengo muchos delegados y eventos para limpiar. ¡Los demonios multicast se han ido!

0

La clase de crianza tiene que obtener una copia nueva del EventHandler. Una posible solución a continuación.

using System; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     class HasEvent 
     { 
      public event EventHandler OnEnvent; 
      EventInvoker myInvoker; 

      public HasEvent() 
      { 
       myInvoker = new EventInvoker(this,() => OnEnvent); 
      } 

      public void MyInvokerRaising() { 
       myInvoker.Raise(); 
      } 

     } 

     class EventInvoker 
     { 
      private Func<EventHandler> GetEventHandler; 
      private object sender; 

      public EventInvoker(object sender, Func<EventHandler> GetEventHandler) 
      { 
       this.sender = sender; 
       this.GetEventHandler = GetEventHandler; 
      } 

      public void Raise() 
      { 
       if(null != GetEventHandler()) 
       { 
        GetEventHandler()(sender, new EventArgs()); 
       } 
      } 
     } 

     static void Main(string[] args) 
     { 
      HasEvent h = new HasEvent(); 
      h.OnEnvent += H_OnEnvent; 
      h.MyInvokerRaising(); 
     } 

     private static void H_OnEnvent(object sender, EventArgs e) 
     { 
      Console.WriteLine("FIRED"); 
     } 
    } 
} 
0

No es una buena programación, pero si quieres hacer eso de ninguna manera se puede hacer algo como esto

class Program 
{ 
    static void Main(string[] args) 
    { 

     Extension ext = new Extension(); 
     ext.MyEvent += ext_MyEvent; 
     ext.Dosomething(); 
    } 

    static void ext_MyEvent(int num) 
    { 
     Console.WriteLine(num); 
    } 
} 


public class Extension 
{ 
    public delegate void MyEventHandler(int num); 
    public event MyEventHandler MyEvent; 

    public void Dosomething() 
    { 
     int no = 0; 
     while(true){ 
      if(MyEvent!=null){ 
       MyEvent(++no); 
      } 
     } 
    } 
} 
+0

Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre cómo y/o por qué resuelve el problema mejoraría la respuesta. valor a largo plazo. – thewaywewere

Cuestiones relacionadas