变量值更改时如何触发事件?


96

我目前正在使用Visual Studio在C#中创建一个应用程序。我想创建一些代码,以便当变量的值为1时,便执行了一段代码。我知道我可以使用if语句,但是问题是该值将在异步过程中更改,因此从技术上讲,可以在更改值之前忽略if语句。

是否可以创建事件处理程序,以便在变量值更改时触发事件?如果是这样,我该怎么做?

我完全有可能误解了if语句的工作原理!任何帮助将非常感激。


1
需要明确的是,仅对于您拥有的变量(或已经与IObservable / INotifyPropertyChanged / Event相关的变量),才可以观察变量的变化。如果不是为了观察系统变量而设计的,则无法观察到它。
心教堂

Answers:


123

在我看来,您想创建一个属性。

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

private int _myProperty;

这使您可以在属性值更改时随时运行一些代码。如果您愿意,可以在这里提出一个活动。


67

每当字段的值要更改时,都可以使用属性设置器引发事件。

您可以拥有自己的EventHandler委托,也可以使用著名的System.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窗体的情况下,可以使用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 2011年

1
_lock对象的用途是什么?
罗德·弗拉明克

2
@LodeVlaeminck防止在处理事件时更改属性的值。
David Suarez

恕我直言,这是一个奇怪的地方。[除非在其他地方也使用了锁,这是不同的情况。]如果两个不同的线程处于争用状态以设置共享属性,则该属性的“最终”状态是不确定的。而是使用某种模式,其中一个线程“拥有”该属性,并且只有他们来设置它。哪种模式取决于情况。(如果确实需要更改线程之间的所有权,请传递警棍/令牌。)如果我在这里遇到了对锁的需要,我将仔细检查整体设计。太太,这里的锁是无害的。
制造商史蒂夫


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!
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.