Как вызвать событие при изменении значения переменной?


96

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

Можно ли создать обработчик событий, чтобы при изменении значения переменной запускалось событие? Если да, то как я могу это сделать?

Вполне возможно, что я мог неправильно понять, как работает оператор if! Любая помощь приветствуется.


1
Чтобы было ясно, наблюдение за изменением переменной возможно только для переменной, которой вы владеете (или которая уже связана с IObservable / INotifyPropertyChanged / Event). Вы не можете наблюдать изменение системной переменной, если оно не предназначено для наблюдения.
Cœur

Ответы:


124

Мне кажется, вы хотите создать недвижимость.

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 Form, которую вы можно использовать BackgourndWorker, чтобы легко и просто выполнять задачи в параллельном потоке.


3
Одно из лучших объяснений во всей сети. Думаю, я наконец понимаю обработку пользовательских событий. Благодарен за этот пост.
Прощай,

44

Платформа .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?
Lode Vlaeminck

2
@LodeVlaeminck предотвращает изменение значения свойства во время обработки события.
Дэвид Суарес,

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