したがって、上記の回答に基づいていくつかの調査を行った後、Google でさらに検索し、C# について少し知っている同僚に尋ねて、問題に対する私が選択した解決策を以下に示します。私はコメント、提案、改良に引き続き関心を持っています。
最初に、問題についてさらに詳しく説明します。これは、GUI が反応しなければならない一連のイベントを通じて、GUI が何かを制御しているという意味で実際にはかなり一般的であり、完全に抽象的である必要があります。いくつかの明確な問題があります。
- さまざまなデータ型のイベント自体。プログラムが進化するにつれて、イベントは追加、削除、変更されます。
- GUI (さまざまな UserControls) を構成するいくつかのクラスと、ハードウェアを抽象化するクラスをブリッジする方法。
- すべてのクラスはイベントを生成および消費でき、可能な限り分離したままにする必要があります。
- コンパイラは、コーディング コックアップを可能な限り特定する必要があります (たとえば、あるデータ型を送信するイベントと別のデータ型を期待するコンシューマ)。
これの最初の部分はイベントです。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);
}
これにより、列挙型の各イベントのイベント ハンドラーが作成されます。
そして、消費する Device オブジェクトのコンストラクターで、次のコードのようにイベントをサブスクライブすることによって消費されます。
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));
}
これには、発生するイベントと消費するイベントのタイプ (ここでは文字列 Filename) が一致しない場合、コンパイラが不平を言うという利点があります。
できることはたくさんありますが、これが問題の本質です。コメントで言ったように、特に明らかな省略/バグまたは欠陥がある場合は、興味があります。これが誰かに役立つことを願っています。