Як викликати подію, коли значення змінної змінюється?


96

Зараз я створюю додаток на C # за допомогою Visual Studio. Я хочу створити деякий код, щоб, коли змінна має значення 1, тоді виконувався певний фрагмент коду. Я знаю, що можу використовувати оператор if, але проблема в тому, що значення буде змінено в асинхронному процесі, тому технічно оператор if можна ігнорувати до того, як значення зміниться.

Чи можна створити обробник подій, щоб при зміні значення змінної спрацьовувала подія? Якщо так, то як я можу це зробити?

Цілком можливо, що я міг неправильно зрозуміти, як працює оператор if! Будь-яка допомога буде дуже вдячна.


1
Щоб зрозуміти, спостерігати за зміною змінної можливо лише для тієї вашої змінної (або яка вже є IObservable / INotifyPropertyChanged / пов'язаною з подією). Ви не можете спостерігати зміну системної змінної, якщо вона не була призначена для спостереження.
Cœur

Відповіді:


123

Мені здається, ви хочете створити власність.

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

private int _myProperty;

Це дозволяє запускати певний код будь-коли, коли змінюється значення властивості. Ви можете підняти тут захід, якщо хочете.


68

Ви можете використовувати установку властивостей, щоб викликати подію, коли значення поля буде змінюватися.

Ви можете мати власного делегата EventHandler або скористатися відомим делегатом System.EventHandler.

Зазвичай для цього існує шаблон:

  1. Визначте публічну подію за допомогою делегата обробника подій (який має аргумент типу EventArgs).
  2. Визначте захищений віртуальний метод, який називається OnXXXXX (наприклад, OnMyPropertyValueChanged). У цьому методі ви повинні перевірити, чи делегат обробника подій є нульовим, і якщо ні, то ви можете його викликати (це означає, що до делегування події приєднано один або кілька методів).
  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
Одне з найкращих пояснень у всій Мережі. Я думаю, що нарешті я розумію спеціальну обробку подій. Дякую за цей пост.
До побачення

43

Структура .NET фактично надає інтерфейс, який ви можете використовувати для сповіщення передплатників про зміну властивості: System.ComponentModel.INotifyPropertyChanged. Цей інтерфейс має одну подію 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 за включення замка, який інші відповіді пропускають.
ctacke

1
Яка користь від об'єкта _lock?
Лоде Влаємінк,

2
@LodeVlaeminck він запобігає зміні вартості властивості під час обробки події.
Девід Суарес,

ІМХО, це дивне місце для замка. [Якщо блокування також не використовується деінде, що є іншою ситуацією.] Якщо два різні потоки перебувають у стані перегонів для встановлення спільної властивості, то «кінцевий» стан властивості не є детермінованим. Замість цього використовуйте шаблон, де один потік "володіє" властивістю, і лише вони встановлюють його. ЯКА закономірність залежить від ситуації. (Якщо справді потрібно змінити власність між потоками, передайте естафету / маркер.) Якщо мені тут довелося зафіксувати замок, я уважно вивчив би загальний дизайн. ОТО, замок тут нешкідливий.
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!
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.