因此,在根据上述答案进行一些研究之后,进一步谷歌搜索并询问一位对 C# 有一点了解的同事,我选择的问题解决方案如下。我仍然对评论、建议和改进感兴趣。
首先是关于这个问题的一些进一步的细节,这实际上是相当通用的,因为 GUI 正在控制某些东西,必须保持完全抽象,通过 GUI 必须对其响应做出反应的一系列事件。有几个明显的问题:
- 事件本身,具有不同的数据类型。随着程序的发展,事件将被添加、删除、更改。
- 如何桥接组成 GUI(不同的用户控件)的多个类和抽象硬件的类。
- 所有类都可以产生和消费事件,并且必须尽可能保持解耦。
- 编译器应尽可能发现编码错误(例如,发送一种数据类型但消费者期望另一种数据类型的事件)
第一部分是事件。由于 GUI 和设备可以引发多个事件,可能具有与之关联的不同数据类型,因此事件调度程序很方便。这在事件和数据中必须是通用的,因此:
// Define a type independent class to contain event data
public class EventArgs<T> : EventArgs
{
public EventArgs(T value)
{
m_value = value;
}
private T m_value;
public T Value
{
get { return m_value; }
}
}
// Create a type independent event handler to maintain a list of events.
public static class EventDispatcher<TEvent> where TEvent : new()
{
static Dictionary<TEvent, EventHandler> Events = new Dictionary<TEvent, EventHandler>();
// Add a new event to the list of events.
static public void CreateEvent(TEvent Event)
{
Events.Add(Event, new EventHandler((s, e) =>
{
// Insert possible default action here, done every time the event is fired.
}));
}
// Add a subscriber to the given event, the Handler will be called when the event is triggered.
static public void Subscribe(TEvent Event, EventHandler Handler)
{
Events[Event] += Handler;
}
// Trigger the event. Call all handlers of this event.
static public void Fire(TEvent Event, object sender, EventArgs Data)
{
if (Events[Event] != null)
Events[Event](sender, Data);
}
}
现在我们需要一些来自 C 世界的事件,我喜欢枚举,所以我定义了一些 GUI 将引发的事件:
public enum DEVICE_ACTION_REQUEST
{
LoadStuffFromXMLFile,
StoreStuffToDevice,
VerifyStuffOnDevice,
etc
}
现在,在 EventDispatcher 的静态类的范围(通常是命名空间)内的任何地方都可以定义一个新的调度程序:
public void Initialize()
{
foreach (DEVICE_ACTION_REQUEST Action in Enum.GetValues(typeof(DEVICE_ACTION_REQUEST)))
EventDispatcher<DEVICE_ACTION_REQUEST>.CreateEvent(Action);
}
这将为枚举中的每个事件创建一个事件处理程序。
并通过在消费设备对象的构造函数中订阅类似以下代码的事件来消费:
public DeviceController( )
{
EventDispatcher<DEVICE_ACTION_REQUEST>.Subscribe(DEVICE_ACTION_REQUEST.LoadAxisDefaults, (s, e) =>
{
InControlThread.Invoke(this, () =>
{
ReadConfigXML(s, (EventArgs<string>)e);
});
});
}
InControlThread.Invoke 是一个抽象类,它简单地包装了调用调用。
GUI 可以简单地引发事件:
private void buttonLoad_Click(object sender, EventArgs e)
{
string Filename = @"c:\test.xml";
EventDispatcher<DEVICE_ACTION_REQUEST>.Fire(DEVICE_ACTION_REQUEST.LoadStuffFromXMLFile, sender, new EventArgs<string>(Filename));
}
这样做的好处是,如果事件引发和消费类型不匹配(这里是字符串文件名),编译器会抱怨。
可以进行许多增强,但这是问题的关键。正如我在评论中所说,我很感兴趣,特别是如果有任何明显的遗漏/错误或缺陷。希望这可以帮助某人。