これを達成する簡単な方法はありません。
推奨されるアプローチ:
デリゲートを保存できない理由がわかりません。インスタンスをフィールドとして保存する必要はありません。これは、匿名イベント ハンドラーによってキャプチャされるローカル変数にすることができます。
EventHandler<TypeOfEventArgs> handler = null;
handler = (s, e) =>
{
// Do whatever you need to do here
// Remove event:
foo.Event -= handler;
}
foo.Event += handler;
これを使用できない単一のシナリオは考えられません。
デリゲートを保存しない代替アプローチ:
ただし、そのようなシナリオがある場合は、非常に注意が必要です。
イベントのハンドラーとして追加されたデリゲートを見つける必要があります。保存していないため、入手がかなり困難です。this
現在実行中のメソッドのデリゲートを取得する必要はありません。
GetInvocationList()
で定義されているクラス外のイベントへのアクセスは、ハンドラーの追加と削除、つまり+=
とに制限されているため、イベントで使用することもできません-=
。
新しいデリゲートを作成することもできません。匿名メソッドを定義するオブジェクトにはアクセスできMethodInfo
ますが、メソッドが宣言されているクラスのインスタンスにはアクセスできません。このクラスはコンパイラによって自動的に生成されthis
、匿名メソッド内で呼び出すと、のインスタンスが返されます。通常のメソッドが定義されているクラス。
私が見つけた唯一の方法は、イベントが使用するフィールドを見つけて呼び出すGetInvocationList()
ことです。次のコードは、ダミー クラスを使用してこれを示しています。
void Main()
{
var foo = new Foo();
foo.Bar += (s, e) => {
Console.WriteLine("Executed");
var self = new StackFrame().GetMethod();
var eventField = foo.GetType()
.GetField("Bar", BindingFlags.NonPublic |
BindingFlags.Instance);
if(eventField == null)
return;
var eventValue = eventField.GetValue(foo) as EventHandler;
if(eventValue == null)
return;
var eventHandler = eventValue.GetInvocationList()
.OfType<EventHandler>()
.FirstOrDefault(x => x.Method == self)
as EventHandler;
if(eventHandler != null)
foo.Bar -= eventHandler;
};
foo.RaiseBar();
foo.RaiseBar();
}
public class Foo
{
public event EventHandler Bar;
public void RaiseBar()
{
var handler = Bar;
if(handler != null)
handler(this, EventArgs.Empty);
}
}
"Bar"
に渡される文字列は、イベントで使用されるフィールドGetField
の正確な名前である必要があることに注意してください。これにより、次の 2 つの問題が発生します。
- 明示的なイベント実装を使用する場合など、フィールドには別の名前を付けることができます。フィールド名を手動で見つける必要があります。
- フィールドがまったくない場合もあります。これは、イベントが明示的なイベント実装を使用し、単に別のイベントに委任するか、他の方法で委任を格納する場合に発生します。
結論:
代替アプローチは実装の詳細に依存するため、回避できる場合は使用しないでください。