事件处理程序和接口

本文关键字:接口 程序 事件处理 | 更新日期: 2023-09-27 18:34:14

我有一个名为IDataIO的接口:

public interface IDataIO
{
  event DataReceivedEvent DataReceived;
  //.....more events,methods and properties
}

我还有多个实现这个接口的类,即UdpIOTcpIOSerialIO

现在,我有一个IO类,允许我在不同的输入/输出硬件之间切换。此类的每个实例都有一个 CurrentIODevice 属性,可以是 SerialIOUdpIOTcpIO 之一。分配此属性后,我将 1 个或多个处理程序附加到DataReceivedEvent,以便在收到传入数据时通知我的 GUI,以及需要通知的其他类。

public class IO
{
  IDataIO CurrentIODevice;
  public IO()
  {
    SerialIO serial = new SerialIO();
    TcpIO tcp = new TcpIO();
    UdpIO udp = new UdpIO();
    CurrentIODevice = serial;
  }
}

我还有一个包含多个IO对象的IOManager类。

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

我关心的(这不是问题 atm(是我将如何在运行时在不同的 IDataIO 接口之间切换。

在运行时执行以下语句的效果是什么:

//i know this is illegal but just to demonstrate
IOManager.Ports[0].CurrentIODevice = tcp; 

事件处理程序是否仍能正常工作(且正确(?

我是否需要在分配 CurrentIODevice 之前取消分配事件,然后在之后再次重新分配处理程序?如果是这种情况,我可以看到这种方法变得非常混乱,所以如果有人有更好的方法来解决这个问题,我都会听:)

事件处理程序和接口

不,您的处理程序将不起作用,因为它们附加到旧对象。接口提供...对象的接口,将其视为一种契约,但它们本身并不是不同的对象。

如果您需要在接口

的不同实现之间切换(在运行时(并保持所有处理程序正常工作,则必须对接口本身具有相同的对象引用,这是一种策略模式(或多或少(。

例如,在您的情况下,您可以在DataIO对象中实现IDataIO接口。它将公开一个属性(或方法,我认为它的意图更明确(以在该接口的不同实现(串行,TCP或其他(之间切换。它将是唯一一个将事件处理程序附加到该接口的对象(当具体实现更改时,它将删除处理程序(。该对象的用户将始终看到它,无论它使用的具体实现是什么。

这是一个解释这个概念的小例子。通用接口如下:

interface IDataIO
{
    void Write(byte[] data);
    byte[] Read();
    event EventHandler DataReceived;
}

这是 IDataIO 的具体实现,其他类将只直接使用这个类:

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

最后是一些用于测试的代码:

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

显然,您应该考虑策略模式。我将首先发布代码,稍后再解释:

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);
}
  1. 接口 IDataIO 定义了所有 IO 类型应实现的基础知识。

  2. 从 IDataIO 派生的 SerialIO、TcpIO、UdpIO 类实现了 SetStrategy(( 方法以满足各自的需求。

  3. IO
  4. 类拥有一个字段(命名端口(是指 IDataIO 类型,该字段可以在运行时通过调用 IO 中定义的方法 SetIOType(( 设置为某个 IO 类型.class。调用此方法后,我们知道"port"字段指的是哪种类型,并且然后调用 SetStrategy(( 方法,它将在其中一个 IO 类中运行被覆盖的方法。

  5. 当它需要某种 IO 类型(比如 SerialIO(时,它只需要新建一个 IO 类,并通过传递一个 SerialIO 类实例调用 SetIOType(( 方法,所有与 SerialIO 类型相关的逻辑都会自动设置。

希望我的描述可以帮到你。