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

#c# #visual-studio #silverlight #windows-phone-7 #event-handling

#c# #visual-studio #silverlight #windows-phone-7 #обработка событий

Вопрос:

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

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

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

Комментарии:

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

Ответ №1:

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

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

private int _myProperty;
  

Это позволяет запускать некоторый код при любом изменении значения свойства. Вы могли бы вызвать событие здесь, если хотите.

Ответ №2:

Вы можете использовать средство установки свойств для инициирования события всякий раз, когда значение поля собирается измениться.

У вас может быть свой собственный делегат EventHandler или вы можете использовать известную систему.Делегат EventHandler.

Обычно для этого есть шаблон:

  1. Определите общедоступное событие с помощью делегата обработчика событий (который имеет аргумент типа EventArgs).
  2. Определите защищенный виртуальный метод с именем OnXXXXX (например,OnMyPropertyValueChanged). В этом методе вы должны проверить, имеет ли значение null делегат обработчика событий, и если нет, вы можете его вызвать (это означает, что к делегированию события подключен один или несколько методов).
  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 для выполнения действий в параллельном потоке красиво и просто.

Комментарии:

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

Ответ №3:

Платформа .NET Framework фактически предоставляет интерфейс, который вы можете использовать для уведомления подписчиков об изменении свойства: System.ComponentModel.INotifyPropertyChanged. В этом интерфейсе изменено одно свойство события. Обычно это используется в 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
        }
    }
}
  

Надеюсь, это полезно 🙂

Комментарии:

1. 1 за включение блокировки, которую опускают другие ответы.

2. Для чего нужен объект _lock ?

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

4. ИМХО, это странное место для блокировки. [Если блокировка также не используется в другом месте, что является другой ситуацией.] Если два разных потока находятся в состоянии гонки для установки общего свойства, то «конечное» состояние свойства не является детерминированным. Вместо этого используйте некоторый шаблон, где один поток «владеет» свойством, и только они устанавливают его. КАКОЙ шаблон зависит от ситуации. (Если действительно нужно сменить владельца между потоками, передайте эстафету / токен.) Если бы я столкнулся с необходимостью блокировки здесь, я бы внимательно изучил общий дизайн. OTOH, блокировка здесь безвредна.

Ответ №4:

просто используйте свойство

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

Ответ №5:

2022

вы можете использовать универсальный класс:

 class Wrapped<T>  {
    private T _value;

    public Action WillChange;
    public Action DidChange;

    public T Value
    {
        get => _value;

        set
        {
            if ( _value != value )
            {
                OnWillChange();
                _value = value;
                OnDidChanged();
            }
        }
    }

    protected virtual void OnWillChange() => WillChange?.Invoke();
    protected virtual void OnDidChange() => DidChange?.Invoke();
}
  

и сможет выполнить следующее:

 var i = new Wrapped<int>();

i.WillChange  = () => { Console.WriteLine("will be changed!"); };
i.DidChange  = () => { Console.WriteLine("changed!"); };

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

Console.ReadKey();
  

Результат:

 will be changed!
changed!
will be changed!
changed!
will be changed!
changed!
will be changed!
changed!
  

Комментарии:

1. Мне лично не нравится, как вы вызываете OnValueChanged(); перед изменением значения. Похоже на ошибку программирования, которая нарушит ваш код

2. Я бы лично проверил, отличается ли новое значение от старого, прежде чем вызывать событие изменения

Ответ №6:

Простой метод включает в себя использование функций get и set для переменной

 
    using System;
    public string Name{
    get{
     return name;
    }
    
    set{
     name= value;
     OnVarChange?.Invoke();
    }
    }
    private string name;
    
    public event System.Action OnVarChange;