Bagaimana cara memicu peristiwa ketika nilai variabel berubah?

96

Saya sedang membuat aplikasi di C # menggunakan Visual Studio. Saya ingin membuat beberapa kode sehingga ketika variabel memiliki nilai 1 maka kode tertentu dijalankan. Saya tahu bahwa saya dapat menggunakan pernyataan if tetapi masalahnya adalah nilainya akan diubah dalam proses asinkron sehingga secara teknis pernyataan if dapat diabaikan sebelum nilainya berubah.

Apakah mungkin untuk membuat penangan peristiwa sehingga ketika nilai variabel berubah, peristiwa dipicu? Jika ya, bagaimana saya bisa melakukan ini?

Sangat mungkin bahwa saya salah memahami cara kerja pernyataan if! Bantuan apa pun akan sangat dihargai.

James Mundy
sumber
1
Untuk memperjelas, mengamati perubahan variabel hanya mungkin untuk variabel yang Anda miliki (atau yang sudah IObservable / INotifyPropertyChanged / Event-related). Anda tidak dapat mengamati perubahan variabel sistem jika tidak dirancang untuk diamati.
Cœur

Jawaban:

124

Sepertinya saya seperti Anda ingin membuat properti.

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

private int _myProperty;

Ini memungkinkan Anda untuk menjalankan beberapa kode setiap kali nilai properti berubah. Anda bisa mengadakan acara di sini, jika Anda mau.

Jonathan Wood
sumber
68

Anda bisa menggunakan penyetel properti untuk memunculkan acara setiap kali nilai bidang akan berubah.

Anda dapat memiliki delegasi EventHandler Anda sendiri atau Anda dapat menggunakan delegasi System.EventHandler yang terkenal.

Biasanya ada pola untuk ini:

  1. Tentukan acara publik dengan delegasi penanganan acara (yang memiliki argumen jenis EventArgs).
  2. Tentukan metode virtual terlindungi yang disebut OnXXXXX (misalnya OnMyPropertyValueChanged). Dalam metode ini, Anda harus memeriksa apakah delegasi event handler null dan jika tidak, Anda dapat memanggilnya (artinya ada satu atau lebih metode yang dilampirkan ke delegasi acara).
  3. Panggil metode terlindungi ini setiap kali Anda ingin memberi tahu pelanggan bahwa ada sesuatu yang berubah.

Berikut contohnya

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

Keuntungan dari pendekatan ini adalah Anda mengizinkan kelas lain yang ingin mewarisi dari kelas Anda untuk mengubah perilaku jika perlu.

Jika Anda ingin menangkap acara di utas berbeda yang dimunculkan, Anda harus berhati-hati agar tidak mengubah status objek yang ditentukan di utas lain yang akan menyebabkan pengecualian utas silang dilempar. Untuk menghindari hal ini, Anda dapat menggunakan metode Panggil pada objek yang ingin Anda ubah statusnya untuk memastikan bahwa perubahan terjadi di utas yang sama dengan peristiwa yang telah dimunculkan atau jika Anda berurusan dengan Formulir Windows Anda dapat menggunakan BackgourndWorker untuk melakukan berbagai hal dalam utas paralel dengan baik dan mudah.

Beatles 1692
sumber
3
Salah satu penjelasan terbaik di seluruh Web. Saya rasa saya akhirnya memahami Penanganan Acara Kustom. Berterima kasih untuk posting ini.
Selamat tinggal
44

Framework .NET sebenarnya menyediakan antarmuka yang dapat Anda gunakan untuk memberi tahu pelanggan saat properti telah berubah: System.ComponentModel.INotifyPropertyChanged. Antarmuka ini memiliki satu acara PropertyChanged. Ini biasanya digunakan di WPF untuk mengikat tetapi saya merasa berguna dalam lapisan bisnis sebagai cara untuk menstandarkan pemberitahuan perubahan properti.

Dalam hal keamanan thread, saya akan mengunci setter agar Anda tidak mengalami kondisi balapan apa pun.

Inilah pemikiran saya dalam kode :):

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

Semoga bermanfaat :)

Daniel Sandberg
sumber
6
1 untuk dimasukkannya gembok yang diabaikan oleh jawaban lain.
ctacke
1
Apa gunanya object _lock?
Lode Vlaeminck
2
@LodeVlaeminck mencegah perubahan nilai properti saat acara sedang diproses.
David Suarez
IMHO, ini adalah tempat yang aneh untuk sebuah gembok. [Kecuali jika kunci juga digunakan di tempat lain, yang situasinya berbeda.] Jika dua utas berbeda berada dalam kondisi balapan untuk menyetel properti bersama, status "akhir" dari properti tersebut tidak deterministik. Sebagai gantinya gunakan beberapa pola di mana satu thread "memiliki" properti, dan hanya mereka yang menyetelnya. Pola yang mana tergantung situasi. (Jika benar-benar perlu mengubah kepemilikan antar utas, berikan tongkat / token.) Jika saya menemui kebutuhan akan kunci di sini, saya akan memeriksa dengan cermat desain keseluruhan. OTOH, kunci di sini tidak berbahaya.
ToolmakerSteve
13

gunakan saja properti

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

Anda dapat menggunakan kelas generik:

class Wrapped<T>  {
    private T _value;

    public Action ValueChanged;

    public T Value
    {
        get => _value;

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

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

dan akan dapat melakukan hal berikut:

var i = new Wrapped<int>();

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

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

Console.ReadKey();

hasil:

changed!
changed!
changed!
changed!
changed!
changed!
changed!
Andrew
sumber