7

PropertyChangedを実装するオブジェクトのイベントをサブスクライブする拡張メソッドがありますINotifyPropertyChanged

イベントが一度だけ発生することを望みます。それ以上ではありません。

これが私の方法です。

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
    if (target == null)
    {
        return;
    }

    PropertyChangedEventHandler handler = (obj, e) =>
    {

        if (propertyName == e.PropertyName)
        {
            action();
        }

    };


    target.PropertyChanged -= handler;
    target.PropertyChanged += handler;

}

しかし、うまくいきません。イベント ハンドラーを削除できないため、このメソッドを呼び出すたびにイベントが発生します。

私は別のアプローチを試みました。匿名メソッドを使用する代わりに、次のようなより伝統的な方法を使用します。

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
    if (target == null)
    {
        return;
    }

    target.PropertyChanged -= target_PropertyChanged;
    target.PropertyChanged += target_PropertyChanged;

}

static void target_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //do stuff here
    }

そして、それはうまく機能します。イベントは 1 回だけ発生しますが、Action パラメーターも必要です。このアプローチでは使用できません。

この問題を解決するための回避策または別のアプローチはありますか?静的メソッド内の匿名メソッドに何か奇妙なことがありますか?

前もって感謝します。

4

3 に答える 3

3

イベントハンドラー自体からサブスクライブ解除すると、最初の例を機能させることができます。

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
    if (target == null)
    {
        return;
    }

    // Declare the handler first, in order to create
    // a concrete reference that you can use from within
    // the delegate
    PropertyChangedEventHandler handler = null;  
    handler = (obj, e) =>
    {
        if (propertyName == e.PropertyName)
        {
            obj.PropertyChanged -= handler; //un-register yourself
            action();
        }

    };
    target.PropertyChanged += handler;
}

上記のコードは、「1つで完了」のイベントハンドラーとして機能します。これらは無制限に登録でき、それぞれは登録を解除する前に1回だけ実行されます。

複数のスレッド間でイベントを短時間連続して発生させると、これらのハンドラーの1つを複数回実行する可能性があることに注意してください。これを防ぐには、静的なDictionary(T、T)マッピングオブジェクトインスタンスを作成して「オブジェクトをロック」し、歩哨コードを追加して、ハンドラーが1回だけ実行されるようにする必要があります。ただし、これらの実装の詳細は、現在書かれているように、質問の範囲から少し外れているようです。

于 2013-02-11T16:52:08.643 に答える
3

これは、匿名メソッドをイベント ハンドラーとして使用する際の制限です。通常のメソッド (技術的には、メソッド グループの変換によって自動的に作成されるデリゲート インスタンス) のように削除することはできません。これは、匿名メソッドがコンパイラによって生成されたコンテナー クラスにコンパイルされ、クラスの新しいインスタンスが毎回作成されるためです。

アクション パラメーターを保持するために、イベント ハンドラーのデリゲートを内部に持つコンテナー クラスを作成できます。クラスは、作業している他のクラスの内部でプライベートに宣言できます。または、おそらく「ヘルパー」名前空間で内部化できます。次のようになります。

class DelegateContainer
{
    public DelegateContainer(Action theAction, string propName)
    {
         TheAction = theAction;
         PopertyName = propName;
    }

    public Action TheAction { get; private set; }
    public string PropertyName { get; private set; }

    public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        if(PropertyName == e.PropertyName)
            TheAction();
    }
}

次に、コンテナへの参照を作成してクラスに保存します。静的メンバーcurrentContainerを作成してから、次のようにハンドラーを設定できます。

private static DelegateContainer currentContainer;

public static void OnPropertyChanged<T>(this  INotifyPropertyChanged target, string    propertyName, Action action)
{
   if (target == null)
   {
       return;
   }

   if(currentContainer != null)         
       target.PropertyChanged -= currentContainer.PropertyChangedHandler;

   currentContainer = new DelegateContainer(action, propertyName);
   target.PropertyChanged += currentContainer.PropertyChangedHandler;
}
于 2013-02-11T16:49:49.560 に答える
0

技術的には、登録を解除しようとしているのと同じ匿名の方法ではありません。.NETは、が呼び出されるたびにそのメソッドの新しいインスタンスを作成しますOnPropertyChanged。そのため、サブスクリプション解除は機能しません。

于 2013-02-11T16:51:20.707 に答える