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