the - Manejo de eventos C#(comparado con Java)
raise event c# (9)
¡Bien, aclaración FINAL !: ¿Entonces esto es lo mejor que puedo hacer en cuanto a código para implementar esos eventos?
public class Computer {
public event EventHandler Started;
public event EventHandler Stopped;
public event EventHandler Reset;
public event EventHandler<BreakPointEvent> BreakPointHit;
public event EventHandler<ExceptionEvent> Error;
public Computer() {
Started = delegate { };
Stopped = delegate { };
Reset = delegate { };
BreakPointHit = delegate { };
Error = delegate { };
}
protected void OnStarted() {
Started(this, EventArgs.Empty);
}
protected void OnStopped() {
Stopped(this, EventArgs.Empty);
}
protected void OnReset() {
Reset(this, EventArgs.Empty);
}
protected void OnBreakPointHit(int breakPoint) {
BreakPointHit(this, new BreakPointEvent(breakPoint));
}
protected void OnError(System.Exception exception) {
Error(this, new ExceptionEvent(exception));
}
}
}
Actualmente estoy teniendo un difícil entendimiento e implementando eventos en C # usando desalteados. Estoy acostumbrado a la forma de hacer las cosas en Java:
- Defina una interfaz para un tipo de oyente que contendría una cantidad de definiciones de métodos
- Definir la clase de adaptador para esa interfaz para facilitar las cosas si no estoy interesado en todos los eventos definidos en un oyente
- Definir métodos Add, Remove y Get [] en la clase que plantea los eventos
- Defina métodos de disparo protegidos para hacer el trabajo sucio de recorrer la lista de oyentes añadidos y llamar al método correcto
Esto lo entiendo (¡y me gusta!) - Sé que podría hacer exactamente lo mismo en c #, pero parece que hay un nuevo sistema (¿mejor?) Para c #. Después de leer innumerables tutoriales que explican el uso de delegados y eventos en c #, aún no estoy más cerca de comprender realmente lo que está sucediendo: S
En resumen, para los siguientes métodos, ¿cómo implementaría el sistema de eventos en c #:
void computerStarted(Computer computer);
void computerStopped(Computer computer);
void computerReset(Computer computer);
void computerError(Computer computer, Exception error);
^ Los métodos anteriores están tomados de una aplicación Java que hice una vez y que estoy tratando de transferir a c #.
¡Muchas muchas gracias!
¡Muchas gracias a todos por sus respuestas! Finalmente estoy empezando a entender lo que está pasando. Sólo una cosa; Parece que si cada evento tuviera un número / tipo diferente de argumentos, necesitaría crear una clase :: EventArgs diferente para manejarlo:
public void computerStarted(Computer computer);
public void computerStopped(Computer computer);
public void computerReset(Computer computer);
public void breakPointHit(Computer computer, int breakpoint);
public void computerError(Computer computer, Exception exception);
¡Esto requeriría tres clases para lidiar con los eventos !? (Bien dos personalizados, y uno que usa la clase predeterminada EventArgs.Empty)
¡Aclamaciones!
Deberá definir un único delegado para ese
public delegate void ComputerEvent(object sender, ComputerEventArgs e);
ComputerEventArgs se definiría así:
public class ComputerEventArgs : EventArgs
{
// TODO wrap in properties
public Computer computer;
public Exception error;
public ComputerEventArgs(Computer aComputer, Exception anError)
{
computer = aComputer;
error = anError;
}
public ComputerEventArgs(Computer aComputer) : this(aComputer, null)
{
}
}
La clase que dispara los eventos tendría estos:
public YourClass
{
...
public event ComputerEvent ComputerStarted;
public event ComputerEvent ComputerStopped;
public event ComputerEvent ComputerReset;
public event ComputerEvent ComputerError;
...
}
Así es como asignas controladores a los eventos:
YourClass obj = new YourClass();
obj.ComputerStarted += new ComputerEvent(your_computer_started_handler);
Su controlador es:
private void ComputerStartedEventHandler(object sender, ComputerEventArgs e)
{
// do your thing.
}
Debería crear cuatro eventos y métodos para subirlos, junto con una nueva clase basada en EventArgs para indicar el error:
public class ExceptionEventArgs : EventArgs
{
private readonly Exception error;
public ExceptionEventArgs(Exception error)
{
this.error = error;
}
public Error
{
get { return error; }
}
}
public class Computer
{
public event EventHandler Started = delegate{};
public event EventHandler Stopped = delegate{};
public event EventHandler Reset = delegate{};
public event EventHandler<ExceptionEventArgs> Error = delegate{};
protected void OnStarted()
{
Started(this, EventArgs.Empty);
}
protected void OnStopped()
{
Stopped(this, EventArgs.Empty);
}
protected void OnReset()
{
Reset(this, EventArgs.Empty);
}
protected void OnError(Exception e)
{
Error(this, new ExceptionEventArgs(e));
}
}
Las clases se suscribirían al evento utilizando un método o una función anónima:
someComputer.Started += StartEventHandler; // A method
someComputer.Stopped += delegate(object o, EventArgs e)
{
Console.WriteLine("{0} has started", o);
};
someComputer.Reset += (o, e) => Console.WriteLine("{0} has been reset");
Algunas cosas para tener en cuenta sobre lo anterior:
- Los métodos OnXXX están protegidos para que las clases derivadas puedan generar los eventos. Esto no siempre es necesario; hazlo como mejor te parezca.
- El
delegate{}
pieza en cada declaración de evento es solo un truco para evitar tener que hacer una comprobación nula. Se suscribe a un controlador de eventos no operativos para cada evento - Las declaraciones de eventos son eventos de tipo campo . Lo que en realidad se está creando es una variable y un evento. Dentro de la clase ves la variable; fuera de la clase ves el evento.
Vea mi artículo de eventos / delegados para obtener más detalles sobre los eventos.
El delegado declara una firma de función, y cuando se usa como un evento en una clase, también actúa como una colección de objetivos de llamada alistados. La sintaxis + = y - = en un evento se usa para agregar un objetivo a la lista.
Dado los siguientes delegados utilizados como eventos:
// arguments for events
public class ComputerEventArgs : EventArgs
{
public Computer Computer { get; set; }
}
public class ComputerErrorEventArgs : ComputerEventArgs
{
public Exception Error { get; set; }
}
// delegates for events
public delegate void ComputerEventHandler(object sender, ComputerEventArgs e);
public delegate void ComputerErrorEventHandler(object sender, ComputerErrorEventArgs e);
// component that raises events
public class Thing
{
public event ComputerEventHandler Started;
public event ComputerEventHandler Stopped;
public event ComputerEventHandler Reset;
public event ComputerErrorEventHandler Error;
}
Te suscribirías a esos eventos con lo siguiente:
class Program
{
static void Main(string[] args)
{
var thing = new Thing();
thing.Started += thing_Started;
}
static void thing_Started(object sender, ComputerEventArgs e)
{
throw new NotImplementedException();
}
}
Aunque los argumentos podrían ser cualquier cosa, el remitente del objeto y EventArgs e es una convención que se usa de manera muy consistente. El + = thing_started primero creará una instancia del delegado apuntando al método objetivo, luego lo agregará al evento.
En el componente en sí, normalmente agregará métodos para activar los eventos:
public class Thing
{
public event ComputerEventHandler Started;
public void OnStarted(Computer computer)
{
if (Started != null)
Started(this, new ComputerEventArgs {Computer = computer});
}
}
Debe probar nulo en caso de que no se hayan agregado delegados al evento. Cuando realice la llamada al método, se llamarán todos los delegados que se hayan agregado. Esta es la razón por la cual para los eventos el tipo de devolución es nulo, no hay un solo valor de retorno, de modo que para retroalimentar la información, usted tendría propiedades en los EventArgs que los manejadores de eventos podrían alterar.
Otro refinamiento sería usar el delegado genérico EventHandler en lugar de declarar un delegado concreto para cada tipo de argumentos.
public class Thing
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerErrorEventArgs> Error;
}
En c # eventos son delegados. Se comportan de forma similar a un puntero de función en C / C ++, pero son clases reales derivadas de System.Delegate.
En este caso, cree una clase personalizada de EventArgs para pasar el objeto Computer.
public class ComputerEventArgs : EventArgs
{
private Computer _computer;
public ComputerEventArgs(Computer computer) {
_computer = computer;
}
public Computer Computer { get { return _computer; } }
}
Luego expone los eventos del productor:
public class ComputerEventProducer
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerEventArgs> Error;
/*
// Invokes the Started event */
private void OnStarted(Computer computer) {
if( Started != null ) {
Started(this, new ComputerEventArgs(computer));
}
}
// Add OnStopped, OnReset and OnError
}
El consumidor de los eventos vincula una función de controlador a cada evento en el consumidor.
public class ComputerEventConsumer
{
public void ComputerEventConsumer(ComputerEventProducer producer) {
producer.Started += new EventHandler<ComputerEventArgs>(ComputerStarted);
// Add other event handlers
}
private void ComputerStarted(object sender, ComputerEventArgs e) {
}
}
Cuando ComputerEventProducer llama a OnStarted, se invoca el evento Started, que a su vez llamará al método ComputerEventConsumer.ComputerStarted.
En primer lugar, hay una firma de método estándar en .Net que se usa generalmente para eventos. Los lenguajes permiten que cualquier tipo de firma de método se use para eventos, y hay algunos expertos que creen que la convención es defectuosa (en general estoy de acuerdo), pero es lo que es y la seguiré para este ejemplo.
- Crea una clase que contendrá los parámetros del evento (derivados de EventArgs).
public class ComputerEventArgs : EventArgs { Computer computer; // constructor, properties, etc. }
- Cree un evento público en la clase que debe activar el evento.
class ComputerEventGenerator // I picked a terrible name BTW. { public event EventHandler<ComputerEventArgs> ComputerStarted; public event EventHandler<ComputerEventArgs> ComputerStopped; public event EventHandler<ComputerEventArgs> ComputerReset; ... }
- Llamar a los eventos.
class ComputerEventGenerator { ... private void OnComputerStarted(Computer computer) { EventHandler<ComputerEventArgs> temp = ComputerStarted; if (temp != null) temp(this, new ComputerEventArgs(computer)); // replace "this" with null if the event is static } }
- Adjunte un controlador para el evento.
void OnLoad() { ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator(); computerEventGenerator.ComputerStarted += new EventHandler<ComputerEventArgs>(ComputerEventGenerator_ComputerStarted); }
- Cree el controlador que acaba de adjuntar (principalmente presionando la tecla Tab en VS).
private void ComputerEventGenerator_ComputerStarted(object sender, ComputerEventArgs args) { if (args.Computer.Name == "HAL9000") ShutItDownNow(args.Computer); }
- No olvide separar el controlador cuando haya terminado. (¡Olvidar hacer esto es la mayor fuente de pérdidas de memoria en C #!)
void OnClose() { ComputerEventGenerator.ComputerStarted -= ComputerEventGenerator_ComputerStarted; }
¡Y eso es!
EDITAR: Honestamente, no puedo entender por qué todos mis puntos numerados aparecen como "1". Odio las computadoras
La principal diferencia es que en C # los eventos no están basados en la interfaz. En su lugar, el editor del evento declara al delegado como un puntero a la función (aunque no exactamente lo mismo :-)). Luego, el suscriptor implementa el prototipo del evento como un método regular y agrega una nueva instancia del delegado a la cadena del editor de eventos. Lea más sobre delegados y eventos .
También puede leer la comparación corta de C # frente a los eventos de Java aquí .
hay varias formas de hacer lo que quieres. La forma más directa sería definir delegados para cada evento en la clase de alojamiento, por ejemplo
public delegate void ComputerStartedDelegate(Computer computer);
protected event ComputerStartedDelegate ComputerStarted;
public void OnComputerStarted(Computer computer)
{
if (ComputerStarted != null)
{
ComputerStarted.Invoke(computer);
}
}
protected void someMethod()
{
//...
computer.Started = true; //or whatever
OnComputerStarted(computer);
//...
}
cualquier objeto puede "escuchar" para este evento simplemente por:
Computer comp = new Computer();
comp.ComputerStarted += new ComputerStartedDelegate(
this.ComputerStartedHandler);
protected void ComputerStartedHandler(Computer computer)
{
//do something
}
La ''forma estándar recomendada'' de hacer esto sería definir una subclase de EventArgs para contener el (los) valor (es) de la Computadora (y el estado / excepción viejo / nuevo), reduciendo 4 delegados a uno. En este caso, esa sería una solución más limpia, esp. con un Enum para la computadora indica en caso de una expansión posterior. Pero la técnica básica sigue siendo la misma:
- el delegado define la firma / interfaz para el controlador / oyente del evento
- el miembro de datos del evento es una lista de "oyentes"
los oyentes se eliminan utilizando la sintaxis - = en lugar de + =