2013-05-24 22 views
8

私がしたいことは、基本的には、関数の名前を知らずにイベントから関数を削除することです。自分自身をイベントハンドラから削除するにはどうすればいいですか?

私はFileSystemWatcherです。ファイルが作成/名前変更された場合、ファイルの名前がチェックされます。一致する場合は、特定の場所に移動します。ただし、ファイルがロックされている場合、タイマーのティックイベントにアタッチするラムダが作成され、ファイルがロックされない限り待機します。そうでない場合は、ファイルを移動し、イベントハンドラから自身を削除します。インスタンスを保持する、名前付きのメソッドを作成するなど、これを行う方法は数多くあります。私はここでどちらかをすることはできません。私の選択肢は何ですか?

+0

。 [this](http://martinfowler.com/eaaDev/EventAggregator.html)をご覧ください。イベントアグリゲータを購読/購読解除する方法について説明しています(この場合は画面についてです)。実際のアグリゲータはオブジェクト間の参照を作成します。そこにはさまざまな実装があります。私が好むものは[Caliburn.Micro](http://www.mindscapehq.com/blog/index.php/2012/02/01/caliburn-micro-part-4-the-event-aggregator/)に実装されています。 。 –

答えて

19

これを達成する簡単な方法はありません。

好ましいアプローチ:

デリゲートを保存することはできませんなぜ私は表示されません。インスタンスをいくつかのフィールドとして保存する必要はありません。それはあなたの匿名のイベントハンドラによって捕獲されたローカル変数になります

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); 
    } 
} 

GetFieldに渡された文字列"Bar"は、イベントで使用されているフィールドの正確な名前にする必要がありますのでご注意ください。この結果、2つの問題が発生する。

  1. フィールドの名前は異なる場合があります。明示的なイベント実装を使用する場合フィールド名は手動で検索する必要があります。
  2. フィールドが全くない可能性があります。これは、イベントが明示的なイベント実装を使用し、別のイベントにデリゲートするか、デリゲートを別の方法で格納する場合に発生します。

結論:

別のアプローチは、実装の詳細に依存しているので、あなたはそれを避けることができれば、それを使用しないでください。

+0

私はclosureを忘れました!うわー!答えがありがとう、あなたは私ができる以上に行きました。 –

+0

ダーク・ファルコンの答えにあなたの前にある否定的なコメントを書いた後、あなたの*好ましい*アプローチで本質的に同じアドバイスをしたことは少し不公平だと思います。 –

+0

@BenVoigt彼のコメントは正しかった。それは技術的には答えではない。たとえ可能であったとしても、それが好ましい解決策だ。また、彼はダークの答えをdownvoteしなかっただけで、それは技術的に記載された基準を満たしていないと言ってコメントした。もう1つの大きな違いは、ダニエルが質問の基準を満たしている解決策を含んでいたことです。 – Servy

0

手順ラムダ式とイベントハンドラを削除するには:あなたは*道結合*のloosleyでイベントをサブスクライブすることができますそこにパターンのカップルがあり

public partial class Form1 : Form 
{ 
    private dynamic myEventHandler; 
    public Form1() 
    { 
     InitializeComponent(); 
    } 
    private void Form1_Load(object sender, EventArgs e) 
    { 
     myEventHandler = new System.EventHandler((sender2, e2) => this.button1_Click(sender, e, "Hi there")); 
     this.button1.Click += myEventHandler; 
    } 

    private void button1_Click(object sender, EventArgs e, string additionalInfo) 
    { 
     MessageBox.Show(additionalInfo); 
     button1.Click -= myEventHandler; 
    } 
} 
関連する問題