suscribirse - eventhandler c# tutorial
Controladores de eventos e interfaces (2)
Tengo una interfaz llamada IDataIO:
public interface IDataIO
{
event DataReceivedEvent DataReceived;
//.....more events,methods and properties
}
También tengo varias clases que implementan esta interfaz, a saber, TcpIO
, SerialIO
, SerialIO
.
Ahora, tengo una clase de IO
que me permite cambiar entre diferentes hardware de entrada / salida. Cada instancia de esta clase tiene una propiedad CurrentIODevice
, que podría ser una de SerialIO
, UdpIO
o TcpIO
. Cuando se asigna esta propiedad, adjunto 1 o más manejadores al DataReceivedEvent
para que se notifique a mi GUI cuando se reciben los datos entrantes, así como a otras clases que necesitan ser notificadas.
public class IO
{
IDataIO CurrentIODevice;
public IO()
{
SerialIO serial = new SerialIO();
TcpIO tcp = new TcpIO();
UdpIO udp = new UdpIO();
CurrentIODevice = serial;
}
}
También tengo una clase de IOManager
que contiene múltiples objetos IO
.
public class IOManager
{
List<IO> Ports = new List<IO>();
public IOManager()
{
Ports.Add(new IO());
Ports.Add(new IO());
}
Ports[0].CurrentIODevice = serial;
Ports[0].CurrentIODevice.DataReceivedHandler += MyGuiUpdate;
Ports[0].CurrentIODevice.DataReceivedHandler += MyDataProcessing;
}
Mi preocupación (no es un problema de la atmósfera) es cómo voy a cambiar entre las diferentes interfaces IDataIO en el tiempo de ejecución.
Cuál es el efecto de, en tiempo de ejecución, realizar la siguiente declaración:
//i know this is illegal but just to demonstrate
IOManager.Ports[0].CurrentIODevice = tcp;
¿Los manejadores de eventos seguirán funcionando (y correctamente)?
¿Debo anular la asignación de los eventos antes de que se asigne el dispositivo CurrentIOD y luego reasignar los controladores nuevamente? Si este es el caso, puedo ver que este enfoque se vuelve bastante complicado, por lo que si alguien tiene un mejor enfoque para este problema, soy todo oídos :)
No, sus manejadores no funcionarán porque están conectados al objeto anterior. Interfaces proporciona ... una interfaz para un objeto, véalo como un tipo de contrato, pero no son un objeto diferente.
Si necesita cambiar entre diferentes implementaciones de la interfaz (en tiempo de ejecución) y para que todos los manejadores funcionen, debe tener la misma referencia de objeto para la interfaz en sí, el tipo de patrón de estrategia (más o menos).
En su caso, puede, por ejemplo, implementar la interfaz IDataIO
en un objeto DataIO
. Expondrá una propiedad (o un método, creo que su intención es más clara) para cambiar entre diferentes implementaciones de esa interfaz (serie, TCP o lo que sea). Será el único objeto para adjuntar un controlador de eventos a esa interfaz (y se eliminará el controlador cuando cambie la implementación concreta). Los usuarios de ese objeto siempre lo verán, independientemente de la implementación concreta que esté utilizando.
Ejemplo
Este es un pequeño ejemplo para explicar este concepto. La interfaz genérica es esta:
interface IDataIO
{
void Write(byte[] data);
byte[] Read();
event EventHandler DataReceived;
}
Esta es la implementación concreta de IDataIO, otras clases usarán solo esta clase directamente:
sealed class DataIO : IDataIO
{
public void SetChannel(IDataIO concreteChannel)
{
if (_concreteChannel != null)
_concreteChannel.DataReceived -= OnDataReceived;
_concreteChannel = concreteChannel;
_concreteChannel.DataReceived += OnDataReceived;
}
public void Write(byte[] data)
{
_concreteChannel.Write(data);
}
public byte[] Read()
{
return _concreteChannel.Read();
}
public event EventHandler DataReceived;
private IDataIO _concreteChannel;
private void OnDataReceived(object sender, EventArgs e)
{
EventHandler dataReceived = DataReceived;
if (dataReceived != null)
dataReceived(this, e);
}
}
Finalmente un código para probar:
class Test
{
public Test()
{
_channel = new TcpIO();
_channel.DataReceived += OnDataReceived;
}
public void SetChannel(IDataIO channel)
{
_channel.SetChannel(channel);
// Nothing will change for this "user" of DataIO
// but now the channel used for transport will be
// the one defined here
}
private void OnDataReceived(object sender, EventArgs e)
{
// You can use this
byte[] data = ((IDataIO)sender).Read();
// Or this, the sender is always the concrete
// implementation that abstracts the strategy in use
data = _channel.Read();
}
private DataIO _channel;
}
Obviamente debes considerar el patrón de estrategia . Voy a publicar el código primero y explicar más adelante:
public interface IDataIO
{
event DataReceivedEvent DataReceived;
//this the new added method that each IO type should implement.
void SetStrategy();
}
public class SerialIO : IDataIO
{
public void SetStrategy()
{
//put the code that related to the Serial IO.
this.DataReceivedHandler += MyGuiUpdate;
this.DataReceivedHandler += MyDataProcessing;
}
}
public class TcpIO : IDataIO
{
public void SetStrategy()
{
//put the code that related to the Tcp IO.
//I will not implement it because it is a demo.
}
}
public class UdpIO : IDataIO
{
public void SetStrategy()
{
//put the code that related to the Udp IO.
//I will not implement it because it is a demo.
}
}
public class IO
{
IDataIO port = new IDataIO();
public void SetIOType(IDataIO ioType)
{
this.port = ioType;
port.SetStrategy();
}
}
public class IOManager
{
List<IO> ports = new List<IO>();
SerialIO serial = new SerialIO();
TcpIO tcp = new TcpIO();
ports[0].SetIOType(serial);
ports[1].SetIOType(tcp);
}
La interfaz IDataIO define los conceptos básicos que deben implementar todos los tipos de IO.
Las clases SerialIO, TcpIO, UdpIO derivadas de IDataIO implementan el método SetStrategy () para satisfacer cada una de sus propias necesidades.
La clase de IO posee un campo (puerto con nombre) se refiere a un tipo IDataIO, este campo se puede configurar para un cierto tipo de IO durante el tiempo de ejecución llamando al método SetIOType () definido en la clase de IO. Una vez que se llama a este método, sabemos a qué tipo se refiere el campo ''puerto'', y luego llamamos al método SetStrategy (), ejecutará el método anulado en una de las clases de IO.
La clase IOManager es el cliente. cuando necesita un cierto tipo de IO, digamos SerialIO, solo necesita una nueva clase de IO y llamar al método SetIOType () pasando una instancia de la clase SerialIO, y toda la lógica relacionada con el tipo SerialIO se configurará automáticamente.
Espero que mi descripción pueda ayudarte.