誰かがイベントにサブスクライブしているかどうかを調べる方法は?
-
05-07-2019 - |
質問
イベントの購読を解除する必要があるユースケースがあります。しかし、サブスクライブを解除する前に、この男が実際にこのイベントに夢中になっているかどうかを確認したいと思います。
これを達成する方法を教えてください?
解決
サンプルクラスPublisherは、1つのイベントPublishを提供します。 IsRegisteredメソッドは、指定されたクラスインスタンスのイベント添付イベントハンドラーを照会し、このクラスインスタンスによって少なくとも1つの登録/添付イベントハンドラーがある場合にtrueを返します。 オーバーライドされたIsRegisteredメソッドは同じことを行いますが、静的型の場合です。
このコードをコンソールアプリケーションプロジェクトに配置し、F5キーを押してデバッグし、試してみます。
internal class Publisher
{
internal event EventHandler<EventArgs> Publish;
internal bool IsRegistered(Type type)
{
if (Publish == null) return false;
//
return (from item in Publish.GetInvocationList() where item.Target == null & item.Method.DeclaringType == type select item).Count() > 0;
}
internal bool IsRegistered(object instance)
{
if (Publish == null) return false;
//
return (from item in Publish.GetInvocationList() where item.Target == instance select item).Count() > 0;
}
static int Main(string[] args)
{
Publisher p = new Publisher();
//
p.Publish += new EventHandler<EventArgs>(static_Publish);
p.Publish += new EventHandler<EventArgs>(p.instance_Publish);
//
Console.WriteLine("eventhandler static_Publish attach: {0}", p.IsRegistered(typeof(Program)));
Console.WriteLine("eventhandler instance_Publish attach: {0}", p.IsRegistered(program));
//
return 0;
}
void instance_Publish(object sender, EventArgs e)
{
}
static void static_Publish(object sender, EventArgs e)
{
}
}`
他のヒント
Microsoft から:
> // Wrap event invocations inside a protected virtual method
// to allow derived classes to override the event invocation behavior
protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
// Event will be null if there are no subscribers
if (handler != null)
{
// Format the string to send inside the CustomEventArgs parameter
e.Message += String.Format(" at {0}", DateTime.Now.ToString());
// Use the () operator to raise the event.
handler(this, e);
}
}
if(handler!= null)部分を探しています。サブスクライバーが存在しない場合は null 、サブスクライバーが存在する場合は null です。
pub / sub環境を想定して、provider.Unsubscribe(EventType、subscriber)を呼び出して、サブスクライバーがサブスクライブしているかどうかをプロバイダーに判断させます
これを行うには2つの方法があります:
- サブスクライブ解除されているデリゲートを削除して新しいデリゲートチェーンを作成し、保存する前に取得したデリゲートと比較できます。 サブスクライブしているデリゲートをサブスクライブ解除する場合、そのデリゲートなしで新しいデリゲートチェーンを取得します。 サブスクライブしていないデリゲートをサブスクライブ解除しようとしている場合、あなたが持っていたものと同じチェーンを取り戻します。
- デリゲートチェーンを手動で調べて、サブスクライブ解除するデリゲートが存在するかどうかを確認できます。これは、簡単にするために
.Contains
などの通常のLinqメソッドを使用して実行できます。
最初のケースは、次のコードのようになります。これにより、一時変数に新しいデリゲートチェーンが作成され、削除するデリゲートを削除して、一時チェーンを既存のチェーンと比較します。同じ場合、デリゲートは存在しませんでした。
private EventHandler _Changed;
public event EventHandler Changed
{
add
{
_Changed += value;
}
remove
{
EventHandler temp = _Changed - value;
if (_Changed == null || temp == _Changed)
throw new InvalidOperationException(
"Delegate is not subscribed, cannot unsubscribe");
_Changed = temp;
}
}
2番目のコードは次のコードのように、購読解除したいデリゲートがデリゲートのチェーンに存在するかどうかを確認するだけです。
private EventHandler _Changed;
public event EventHandler Changed
{
add
{
_Changed += value;
}
remove
{
if (_Changed == null || !_Changed.GetInvocationList().Contains(value))
throw new InvalidOperationException(
"Delegate is not subscribed, cannot unsubscribe");
_Changed -= value;
}
}
必要に応じて、デリゲートが2回追加されるケースを処理するために同様のコードを使用できることに注意してください。