C# 事件处理程序和接口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10614478/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Event Handlers and Interfaces
提问by Simon
I have an interface called IDataIO:
我有一个名为 IDataIO 的接口:
public interface IDataIO
{
event DataReceivedEvent DataReceived;
//.....more events,methods and properties
}
I also have multiple classes that implement this interface, namely UdpIO, TcpIO, SerialIO.
我还有多个实现此接口的类,即UdpIO, TcpIO, SerialIO。
Now, I have an IOclass that allows me to switch between different input/output hardware. Each instance of this class has a CurrentIODeviceproperty, which could be one of SerialIO,UdpIOor TcpIO. When this property is assigned, i attach 1 or more handlers to the DataReceivedEventso that my GUI is notified when incoming data is received, as well as other classes that need to be notified.
现在,我有一个IO类可以让我在不同的输入/输出硬件之间切换。此类的每个实例都有一个CurrentIODevice属性,该属性可以是SerialIO、UdpIO或 之一TcpIO。分配此属性后,我将 1 个或多个处理程序附加到 ,DataReceivedEvent以便在接收到传入数据时通知我的 GUI,以及需要通知的其他类。
public class IO
{
IDataIO CurrentIODevice;
public IO()
{
SerialIO serial = new SerialIO();
TcpIO tcp = new TcpIO();
UdpIO udp = new UdpIO();
CurrentIODevice = serial;
}
}
I also have a IOManagerclass that holds multiple IOobjects.
我还有一个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;
}
My concern (its not an issue atm) is how I am going to change between different IDataIO interfaces at runtime.
我担心(它不是 atm 问题)是我将如何在运行时在不同的 IDataIO 接口之间进行更改。
What is the effect of, at runtime, performing the following statement:
在运行时执行以下语句的效果是什么:
//i know this is illegal but just to demonstrate
IOManager.Ports[0].CurrentIODevice = tcp;
Will the event handlers still be functioning (and correctly)?
事件处理程序是否仍然正常运行(并且正确)?
Do i need to unassign the events before the CurrentIODevice is assigned, and then re-assign the handlers again after? If this is the case, I can see this approach getting quite messy, so if anyone has a better approach to this problem I'm all ears :)
我是否需要在分配 CurrentIODevice 之前取消分配事件,然后再次重新分配处理程序?如果是这种情况,我可以看到这种方法变得非常混乱,所以如果有人有更好的方法来解决这个问题,我会全力以赴:)
采纳答案by Adriano Repetti
No, your handlers will not workbecause they're attached to the old object. Interfaces provides...an interface to an object, see it as a kind of contractbut they're not a different object themselves.
不,您的处理程序将无法工作,因为它们附加到旧对象。接口提供...一个对象的接口,将其视为一种契约,但它们本身并不是一个不同的对象。
If you need to switch between different implementations of the interface (at run-time) and to keep all handlers working you have to have the same object reference for the interface itself, kind of strategy pattern(more or less).
如果您需要在接口的不同实现之间切换(在运行时)并保持所有处理程序正常工作,您必须对接口本身具有相同的对象引用,一种策略模式(或多或少)。
In your case you may, for example, implement the IDataIOinterface in a DataIOobject. It'll expose a property (or a method, I think its intent is more clear) to switch between different implementations of that interface (serial, TCP or whatever). It'll be the only one object to attach an event handler to that interface (and it'll drop the handler when the concrete implementation will change). Users of that object will always see it, whatever it's the concrete implementation it's using.
例如,在您的情况下,您可以IDataIO在DataIO对象中实现接口。它将公开一个属性(或一个方法,我认为它的意图更明确)以在该接口的不同实现(串行、TCP 或其他)之间切换。它将是唯一一个将事件处理程序附加到该接口的对象(当具体实现发生变化时,它将删除处理程序)。该对象的用户将始终看到它,无论它使用的是什么具体实现。
Example
例子
This is a small example to explain this concept. The generic interface is this:
这是一个解释这个概念的小例子。通用接口是这样的:
interface IDataIO
{
void Write(byte[] data);
byte[] Read();
event EventHandler DataReceived;
}
This is the concrete implementation of IDataIO, other classes will use only this class directly:
这是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);
}
}
Finally some code for testing:
最后一些用于测试的代码:
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;
}
回答by ugoa
Obviously you should consider the strategy pattern. I will post the code first and explain later:
显然你应该考虑策略模式。我先贴出代码,稍后解释:
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);
}
The interface IDataIO define basics that the all the IO types should implement.
The SerialIO, TcpIO, UdpIO classes derived from IDataIO implement the method SetStrategy() to meet each of their own need.
The IO class owns a field(named port) refers to a IDataIO type, this field can be setted to a certain IO type during the runtime by calling the method SetIOType() defined in the IO class. Once this method is being called, we know which type the 'port' field refers to, and then call the SetStrategy() method, it will run the overrided method in one of the IO class.
The IOManager class is the client. when it needs a certain IO type, say SerialIO, it only need to new a IO class and call the SetIOType() method by passing a SerialIO class instance, and all the logic related to the SerialIO type will be setted automatically.
接口 IDataIO 定义了所有 IO 类型都应该实现的基础知识。
从 IDataIO 派生的 SerialIO、TcpIO、UdpIO 类实现了 SetStrategy() 方法以满足它们各自的需要。
IO 类拥有一个字段(命名端口)是指一个 IDataIO 类型,可以在运行时通过调用 IO 类中定义的方法 SetIOType() 将该字段设置为某个 IO 类型。一旦这个方法被调用,我们就知道'port'字段指的是哪种类型,然后调用SetStrategy()方法,它将在IO类之一中运行被覆盖的方法。
IOManager 类是客户端。当需要某种IO类型,比如SerialIO时,只需要新建一个IO类,传入一个SerialIO类实例调用SetIOType()方法,所有与SerialIO类型相关的逻辑都会自动设置。
Hope my description can help you.
希望我的描述能帮到你。

