変数の値が変更されたときにイベントをトリガーする方法は?


96

現在、Visual Studioを使用してC#でアプリケーションを作成しています。変数の値が1のときに特定のコードが実行されるように、コードを作成したいと思います。ifステートメントを使用できることはわかっていますが、問題は非同期プロセスで値が変更されるため、技術的には値が変更される前にifステートメントを無視できることです。

変数の値が変化したときにイベントがトリガーされるようにイベントハンドラーを作成することはできますか?もしそうなら、どうすればこれを行うことができますか?

ifステートメントがどのように機能するかを誤解していた可能性があります。どんな助けでも大歓迎です。


1
明確にするために、変数の変更の監視は、所有している(または既にIObservable / INotifyPropertyChanged / Event関連の)変数に対してのみ可能です。システム変数の変化を監視するように設計されていない場合は、変更を監視できません。
2014

回答:


123

プロパティを作成したいようです。

public int MyProperty
{
    get { return _myProperty; }
    set
    {
        _myProperty = value;
        if (_myProperty == 1)
        {
            // DO SOMETHING HERE
        }
    }
}

private int _myProperty;

これにより、プロパティ値が変更されるたびにコードを実行できます。必要に応じて、ここでイベントを発生させることができます。


67

プロパティセッターを使用して、フィールドの値が変更されるたびにイベントを発生させることができます。

独自のEventHandlerデリゲートを持つことも、有名なSystem.EventHandlerデリゲートを使用することもできます。

通常、これにはパターンがあります。

  1. イベントハンドラデリゲート(EventArgs型の引数を持つ)を使用してパブリックイベントを定義します。
  2. OnXXXXX(OnMyPropertyValueChangedなど)と呼ばれる保護された仮想メソッドを定義します。このメソッドでは、イベントハンドラーデリゲートがnullであるかどうかを確認し、そうでない場合はそれを呼び出すことができます(つまり、イベントデリゲートに1つ以上のメソッドがアタッチされていることを意味します)。
  3. 何かが変更されたことをサブスクライバーに通知する場合は常に、この保護されたメソッドを呼び出します。

ここに例があります

private int _age;

//#1
public event System.EventHandler AgeChanged;

//#2
protected virtual void OnAgeChanged()
{ 
     if (AgeChanged != null) AgeChanged(this,EventArgs.Empty); 
}

public int Age
{
    get
    {
         return _age;
    }

    set
    {
         //#3
         _age=value;
         OnAgeChanged();
    }
 }

このアプローチの利点は、クラスから継承したい他のクラスが必要に応じて動作を変更できるようにすることです。

発生している別のスレッドでイベントをキャッチする場合は、別のスレッドで定義されているオブジェクトの状態を変更しないように注意する必要があります。これにより、スレッド間例外がスローされます。これを回避するには、状態を変更するオブジェクトでInvokeメソッドを使用して、イベントが発生したスレッドと同じスレッドで変更が発生していることを確認するか、Windowsフォームを処理している場合にBackgourndWorkerを使用して、並列スレッドで素晴らしく簡単に処理を行うことができます。


3
Web全体で最も優れた説明の1つ。ようやくカスタムイベント処理について理解できたと思います。この投稿に感謝します。
さようなら

43

.NETフレームワークは、プロパティが変更されたときにサブスクライバーに通知するために使用できるインターフェイスを実際に提供します:System.ComponentModel.INotifyPropertyChanged。このインターフェイスには、1つのイベントPropertyChangedがあります。これは通常、バインドのためにWPFで使用されますが、プロパティ変更通知を標準化する方法としてビジネスレイヤーで役立つことがわかりました。

スレッドセーフティに関しては、セッターにロックをかけて、競合状態に陥らないようにします。

ここにコードでの私の考えがあります:):

public class MyClass : INotifyPropertyChanged
{
    private object _lock;

    public int MyProperty
    {
        get
        {
            return _myProperty;
        }
        set
        {
            lock(_lock)
            {
                //The property changed event will get fired whenever
                //the value changes. The subscriber will do work if the value is
                //1. This way you can keep your business logic outside of the setter
                if(value != _myProperty)
                {
                    _myProperty = value;
                    NotifyPropertyChanged("MyProperty");
                }
            }
        }
    }

    private NotifyPropertyChanged(string propertyName)
    {
        //Raise PropertyChanged event
    }
    public event PropertyChangedEventHandler PropertyChanged;
}


public class MySubscriber
{
    private MyClass _myClass;        

    void PropertyChangedInMyClass(object sender, PropertyChangedEventArgs e)
    {
        switch(e.PropertyName)
        {
            case "MyProperty":
                DoWorkOnMyProperty(_myClass.MyProperty);
                break;
        }
    }

    void DoWorkOnMyProperty(int newValue)
    {
        if(newValue == 1)
        {
             //DO WORK HERE
        }
    }
}

これが役に立てば幸い:)


6
他の回答が省略したロックを含めるための+1。
2011

1
オブジェクト_lockの用途は何ですか?
Lode Vlaeminck、2015年

2
@LodeVlaeminckは、イベントの処理中にプロパティの値が変更されるのを防ぎます。
David Suarez

私見、これはロックの奇妙な場所です。[ロックが別の場所でも使用されている場合を除き、状況は異なります。]共有プロパティを設定するために2つの異なるスレッドが競合状態にある場合、プロパティの「最終」状態は確定的ではありません。代わりに、1つのスレッドがプロパティを「所有」し、それらのみがプロパティを設定するパターンを使用します。どのパターンが状況に依存します。(スレッド間で所有権を本当に変更する必要がある場合は、バトン/トークンを渡します。)ここでロックが必要になった場合は、設計全体を注意深く調べます。OTOH、ここのロックは無害です。
ToolmakerSteve

13

プロパティを使用するだけ

int  _theVariable;
public int TheVariable{
  get{return _theVariable;}
  set{
    _theVariable = value; 
    if ( _theVariable == 1){
      //Do stuff here.
    }
  }
}

0

あなたはジェネリッククラスを使うことができます:

class Wrapped<T>  {
    private T _value;

    public Action ValueChanged;

    public T Value
    {
        get => _value;

        set
        {
            OnValueChanged();
            _value = value;
        }
    }

    protected virtual void OnValueChanged() => ValueChanged?.Invoke() ;
}

次のことができるようになります:

var i = new Wrapped<int>();

i.ValueChanged += () => { Console.WriteLine("changed!"); };

i.Value = 10;
i.Value = 10;
i.Value = 10;
i.Value = 10;

Console.ReadKey();

結果:

changed!
changed!
changed!
changed!
changed!
changed!
changed!
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.