2011-07-26 20 views
1

以下のメソッドは、派生クラスがイベントによって通知される必要があるオブジェクトを指定できるようにする基本クラスの一部です。匿名メソッドのターゲットを取得する

protected void RaiseEvent<TEventArgs>(EventHandler<TEventArgs> updateEvent, TEventArgs eventArgs, UpdateReceivers updateReceivers) 
    where TEventArgs : EventArgs 
    { 
    EventHandler<TEventArgs> handler = updateEvent; 
    if (handler != null) 
    { 
     if (updateReceivers.ToAllSubscribers) 
     { 
      handler(this, eventArgs); 
     } 
     else 
     { 
      NotifySpecifiedReceiver(handler, eventArgs, updateReceivers.Receiver); 
     } 
    } 
    } 

    private void NotifySpecifiedReceiver<TEventArgs>(EventHandler<TEventArgs> handler, TEventArgs eventArgs, object updateReceiver) 
    where TEventArgs : EventArgs 
    { 
    foreach (Delegate @delegate in handler.GetInvocationList()) 
    { 
     // is the delegates target our receiver? 
     // but this doesnt work for anonymous methods :(
     if (@delegate.Target == updateReceiver) 
     { 
      try 
      { 
       @delegate.DynamicInvoke(this, eventArgs); 
      } 
      catch (Exception ex) 
      { 
      } 
     } 
    } 
    } 

のみ特定の受信機に通知するために、この方法は、このように使用されている:

event EventHandler<SomethingChangedEventArgs> SomethingChanged; 

RaiseEvent(SomethingChanged, args, UpdateReceivers.Single(receiver)); 

これは受信機に「ポインティング」デリゲートを上昇させる(受信機はもちろん加入しなければなりません) 。

私の悩みは、匿名メソッドを使用してSomethingChangedイベントを購読し、このイベントを使用して購読しているオブジェクトに通知するときに問題が発生することです。

class EventConsumer 
{ 
    private EventSource _eventSource; 

    private void SubscribeEvents() 
    { 
     // ReactOnEvent() will not be called because the check [@delegate.Target == updateReceiver] doesnt work for anonymous methods 
     _eventSource.MyStateChanged += (sender, e) => ReactOnEvent(); 

     _eventSource.PublishCurrentState(this); 
    } 
} 

class EventSource 
{ 
    // multiple objects are subscribed to this event 
    event EventHandler<MyStateChangedEventArgs> MyStateChanged; 

    public void GetCurrentState(object receiver) 
    { 
     // receiver ask for the current state, only raise event for him 
     RaiseEvent(MyStateChanged, args, UpdateReceivers.Single(receiver)); 
    } 
} 

匿名メソッドを含むインスタンスを取得することは可能ですか?または私は私の問題を解決するために完全に異なるアプローチを使用すべきですか?

+0

あなたは 'DynamicInvoke'を呼び出すべきではありません。 – SLaks

+0

デリゲートを呼び出す別の方法はありますか? – leozilla

+0

'@delegate(this、eventArgs)'型指定されていないデリゲートには 'DynamicInvoke'が必要です。 (これはメソッドを呼び出すためにリフレクションを使用します) – SLaks

答えて

4

匿名メソッドで使用される変数を含むcompiler-generated closure classが表示されます(おそらく)。
このクラスは、親クラスの情報を参照するspecifically-named fieldsを持っています。

リフレクションを使用して、<>4__thisという名前のDisplayClassTarget値)というコンパイラでフィールドを検索し、その値を取得してデリゲートを作成したクラスのインスタンスを見つけることができます。

です。
これは、いつでも変更される可能性のあるC#コンパイラの内部動作に依存します。

また、クロージャークラスに含まれるフィールドは、匿名メソッドの場所と、そのメンバーが参照するメンバーによって異なります。匿名メソッドがクラスインスタンスを使用しない場合は、thisフィールドがまったくない可能性があります。

+0

私はここで匿名の方法を使用することは不可能であるという事実と一緒に生きなければならないと思います。代理人が反射を使用せずに匿名の方法であるかどうかを調べる方法はありますか?これは、誰かが匿名メソッドを使って購読するときにログエントリを追加するのに役立ちます。 – leozilla

+1

'Target'が' [CompilerGenerated] 'であるかどうかを確認してください。 – SLaks

関連する問題