これを行う最良の方法はSomeClass
、を介して同期コンテキストを渡すことISynchronizeInvoke
です。
public class SomeClass
{
public event EventHandler SmartSyncEvent;
public ISynchronizeInvoke SynchronizingObject { get; set; }
public void RaiseSmartSyncEvent()
{
if (SynchronizingObject != null)
{
SynchronizingObject.Invoke(
(Action)(()=>
{
SmartSyncEvent();
}), null);
}
else
{
SmartSyncEvent();
}
}
}
System.Timers.Timer
このパターンは、実装方法に似ています。これに伴う問題は、すべてのサブスクライバーが同じ同期オブジェクトにマーシャリングされることです。しかし、これがあなたが望むものではないようです。
幸いなことに、デリゲートは、プロパティを介してメソッドを呼び出す必要があるクラスインスタンスを格納しTarget
ます。デリゲートを呼び出す前に抽出し、もちろんそれ自体であると想定して同期オブジェクトとして使用することで、これを悪用できISynchronizeInvoke
ます。カスタムの追加および削除イベントアクセサーを使用することで、実際にそれを実施できます。
public class SomeClass
{
private EventHandler _SmartSyncEvent;
public event EventHandler SmartSyncEvent
{
add
{
if (!(value.Target is ISynchronizeInvoke))
{
throw new ArgumentException();
}
_SmartSyncEvent = (EventHandler)Delegate.Combine(_SmartSyncEvent, value);
}
remove
{
_SmartSyncEvent = (EventHandler)Delegate.Remove(_SmartSyncEvent, value);
}
}
public void RaiseMyEvent()
{
foreach (EventHandler handler in _SmartSyncEvent.GetInvocationList())
{
var capture = handler;
var synchronizingObject = (ISynchronizeInvoke)handler.Target;
synchronizingObject.Invoke(
(Action)(() =>
{
capture(this, new EventArgs());
}), null);
}
}
}
これは、各サブスクライバーを他のサブスクライバーから独立してマーシャリングできるという点ではるかに優れています。それに関する問題は、ハンドラーがISynchronizeInvoke
クラスに存在するインスタンスメソッドでなければならないということです。また、Delegate.Target
静的メソッドの場合はnullです。add
これが、カスタムアクセサーでその制約を適用する理由です。
Delegate.Target
nullの場合、または有用な同期オブジェクトにキャストできない場合は、ハンドラーを同期的に実行させることができます。このテーマにはたくさんのバリエーションがあります。
DispatcherObject
WPFでは、の代わりにをコーディングできますISynchronizeInvoke
。