21

我有一个名为 IDataIO 的接口:

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

我也有多个实现这个接口的类,即UdpIO, TcpIO, SerialIO

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

public class IO
{
  IDataIO CurrentIODevice;

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

我还有一个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;
}

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

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

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

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

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

4

2 回答 2

18

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

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

例如,在您的情况下,您可以在对象中实现IDataIO接口。DataIO它将公开一个属性(或方法,我认为它的意图更清楚)以在该接口的不同实现(串行、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;
}
于 2012-05-16T08:16:22.367 回答
3

显然你应该考虑策略模式。我先贴出代码,稍后再解释:

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类拥有一个字段(命名端口),引用一个IDataIO类型,该字段可以在运行时通过调用IO类中定义的SetIOType()方法设置为某种IO类型。一旦调用此方法,我们就知道“端口”字段所指的类型,然后调用 SetStrategy() 方法,它将在其中一个 IO 类中运行被覆盖的方法。

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

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

于 2012-05-16T09:39:34.080 回答