事件处理程序和接口

Sim*_*mon 19 .net c# events interface

我有一个名为IDataIO的界面:

public interface IDataIO
{
  event DataReceivedEvent DataReceived;
  //.....more events,methods and properties
}
Run Code Online (Sandbox Code Playgroud)

我也有一个实现此接口,即多个类UdpIO,TcpIO,SerialIO.

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

public class IO
{
  IDataIO CurrentIODevice;

  public IO()
  {
    SerialIO serial = new SerialIO();
    TcpIO tcp = new TcpIO();
    UdpIO udp = new UdpIO();
    CurrentIODevice = serial;
  }
}
Run Code Online (Sandbox Code Playgroud)

我还有一个IOManager包含多个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;
}
Run Code Online (Sandbox Code Playgroud)

我关心的问题(不是问题atm)是我如何在运行时在不同的IDataIO接口之间进行更改.

在运行时执行以下语句的影响是什么:

//i know this is illegal but just to demonstrate
IOManager.Ports[0].CurrentIODevice = tcp; 
Run Code Online (Sandbox Code Playgroud)

事件处理程序是否仍在运行(并且正确)?

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

Adr*_*tti 16

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

如果您需要在接口的不同实现之间切换(在运行时)并且为了保持所有处理程序的工作,您必须为接口本身提供相同的对象引用,这种策略模式(或多或少).

例如,在您的情况下,您可以IDataIODataIO对象中实现接口.它将公开一个属性(或一种方法,我认为它的意图更清楚)在该接口的不同实现之间切换(串行,TCP或其他).它将是唯一一个将事件处理程序附加到该接口的对象(当具体实现发生变化时,它将删除处理程序).该对象的用户将始终看到它,无论它是否正在使用它的具体实现.

这是解释这个概念的一个小例子.通用接口是这样的:

interface IDataIO
{
    void Write(byte[] data);

    byte[] Read();

    event EventHandler DataReceived;
}
Run Code Online (Sandbox Code Playgroud)

这是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一些测试代码:

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;
}
Run Code Online (Sandbox Code Playgroud)