Làm thế nào để kích hoạt sự kiện khi giá trị của một biến bị thay đổi?


96

Tôi hiện đang tạo một ứng dụng trong C # bằng Visual Studio. Tôi muốn tạo một số mã để khi một biến có giá trị là 1 thì một đoạn mã nhất định được thực hiện. Tôi biết rằng tôi có thể sử dụng câu lệnh if nhưng vấn đề là giá trị sẽ bị thay đổi trong một quy trình không đồng bộ nên về mặt kỹ thuật, câu lệnh if có thể bị bỏ qua trước khi giá trị thay đổi.

Có thể tạo một trình xử lý sự kiện để khi giá trị biến thay đổi, một sự kiện được kích hoạt không? Nếu vậy, tôi có thể làm như thế nào?

Tôi hoàn toàn có thể hiểu sai cách hoạt động của câu lệnh if! Bất kì sự trợ giúp nào đều được đánh giá cao.


1
Nói rõ hơn, việc quan sát sự thay đổi của một biến chỉ có thể thực hiện được đối với biến mà bạn sở hữu (hoặc đã có liên quan đến IObservable / INotifyPropertyChanged / Event). Bạn không thể quan sát sự thay đổi của biến hệ thống nếu nó không được thiết kế để quan sát.
Cœur

Câu trả lời:


123

Có vẻ như tôi muốn bạn tạo một tài sản.

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

private int _myProperty;

Điều này cho phép bạn chạy một số mã bất kỳ lúc nào giá trị thuộc tính thay đổi. Bạn có thể nêu ra một sự kiện ở đây, nếu bạn muốn.


67

Bạn có thể sử dụng bộ thiết lập thuộc tính để nâng cao sự kiện bất cứ khi nào giá trị của trường thay đổi.

Bạn có thể có đại biểu EventHandler của riêng mình hoặc bạn có thể sử dụng đại biểu System.EventHandler nổi tiếng.

Thông thường có một mẫu cho điều này:

  1. Xác định sự kiện công khai với đại biểu trình xử lý sự kiện (có đối số kiểu EventArgs).
  2. Xác định một phương thức ảo được bảo vệ có tên là OnXXXXX (ví dụ: OnMyPropertyValueChanged). Trong phương thức này, bạn nên kiểm tra xem ủy quyền của trình xử lý sự kiện có rỗng không và nếu không, bạn có thể gọi nó (có nghĩa là có một hoặc nhiều phương thức được đính kèm với ủy quyền sự kiện).
  3. Gọi phương thức được bảo vệ này bất cứ khi nào bạn muốn thông báo cho người đăng ký rằng có điều gì đó đã thay đổi.

Đây là một ví dụ

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

Ưu điểm của phương pháp này là bạn cho phép bất kỳ lớp nào khác muốn kế thừa từ lớp của bạn thay đổi hành vi nếu cần.

Nếu bạn muốn bắt một sự kiện trong một luồng khác mà nó đang được đưa lên, bạn phải cẩn thận không thay đổi trạng thái của các đối tượng được xác định trong một luồng khác, điều này sẽ gây ra một ngoại lệ luồng chéo. Để tránh điều này, bạn có thể sử dụng phương thức Gọi trên đối tượng mà bạn muốn thay đổi trạng thái của nó để đảm bảo rằng thay đổi đang diễn ra trong cùng một chuỗi mà sự kiện đã được nêu ra hoặc trong trường hợp bạn đang xử lý Biểu mẫu Windows, bạn có thể sử dụng BackgourndWorker để thực hiện mọi việc theo một chuỗi song song một cách dễ dàng và dễ dàng.


3
Một trong những lời giải thích hay nhất trên toàn bộ trang Web. Tôi nghĩ rằng cuối cùng tôi cũng hiểu được Xử lý sự kiện tùy chỉnh. Cảm ơn cho bài viết này.
Tạm biệt

43

Khuôn khổ .NET thực sự cung cấp một giao diện mà bạn có thể sử dụng để thông báo cho người đăng ký khi thuộc tính đã thay đổi: System.ComponentModel.INotifyPropertyChanged. Giao diện này có một sự kiện PropertyChanged. Nó thường được sử dụng trong WPF để ràng buộc nhưng tôi thấy nó hữu ích trong các lớp nghiệp vụ như một cách để chuẩn hóa thông báo thay đổi thuộc tính.

Về mặt an toàn luồng, tôi sẽ đặt một khóa dưới bộ cài để bạn không gặp phải bất kỳ điều kiện đua nào.

Đây là suy nghĩ của tôi trong mã :):

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

Hy vọng điều này là hữu ích :)


6
+1 cho việc bao gồm khóa mà các câu trả lời khác bỏ qua.
ctacke

1
Công dụng của object _lock là gì?
Lode Vlaeminck

2
@LodeVlaeminck nó ngăn việc thay đổi giá trị của thuộc tính trong khi sự kiện đang được xử lý.
David Suarez

IMHO, đây là một nơi kỳ lạ cho một ổ khóa. [Trừ khi khóa cũng được sử dụng ở nơi khác, đó là một tình huống khác.] Nếu hai luồng khác nhau đang trong điều kiện chạy đua để thiết lập thuộc tính chung, thì trạng thái "cuối cùng" của thuộc tính không xác định. Thay vào đó, hãy sử dụng một số mẫu trong đó một luồng "sở hữu" thuộc tính và chỉ chúng thiết lập nó. Mô hình nào phụ thuộc vào tình huống. (Nếu thực sự cần thay đổi quyền sở hữu giữa các luồng, hãy chuyển một baton / mã thông báo.) Nếu tôi gặp phải trường hợp cần khóa ở đây, tôi sẽ kiểm tra kỹ thiết kế tổng thể. OTOH, khóa ở đây là vô hại.
ToolmakerSteve

13

chỉ sử dụng một tài sản

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

0

bạn có thể sử dụng lớp chung:

class Wrapped<T>  {
    private T _value;

    public Action ValueChanged;

    public T Value
    {
        get => _value;

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

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

và sẽ có thể làm những việc sau:

var i = new Wrapped<int>();

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

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

Console.ReadKey();

kết quả:

changed!
changed!
changed!
changed!
changed!
changed!
changed!
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.