【问题标题】:How to trigger event when a variable's value is changed?当变量的值改变时如何触发事件?
【发布时间】:2011-04-30 14:21:30
【问题描述】:

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

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

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

【问题讨论】:

  • 为了清楚起见,观察一个变量的变化只有你拥有的一个变量是可能的(或者它已经是 IObservable/INotifyPropertyChanged/Event 相关的)。如果系统变量的变化不是为了观察而设计的,那么您就无法观察到它。

标签: c# visual-studio silverlight windows-phone-7 event-handling


【解决方案1】:

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

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

private int _myProperty;

这允许您在属性值更改时运行一些代码。如果您愿意,可以在这里发起活动。

【讨论】:

    【解决方案2】:

    只要字段的值发生变化,您就可以使用属性设置器引发事件。

    您可以拥有自己的 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】:

    .NET 框架实际上提供了一个接口,您可以使用该接口在属性发生更改时通知订阅者:System.ComponentModel.INotifyPropertyChanged。此接口有一个事件 PropertyChanged。它通常在 WPF 中用于绑定,但我发现它在业务层中作为一种标准化属性更改通知的方式很有用。

    在线程安全方面,我会在 setter 中加一个锁,这样您就不会遇到任何竞争条件。

    这是我在代码中的想法:):

    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 表示包含其他答案省略的锁。
    • 对象_lock有什么用?
    • @LodeVlaeminck 它可以防止在处理事件时更改属性的值。
    • 恕我直言,这是一个奇怪的地方锁。 [除非锁也在其他地方使用,这是不同的情况。] 如果两个不同的线程处于竞争条件以设置共享属性,则该属性的“最终”状态不是确定性的。而是使用一些模式,其中一个线程“拥有”该属性,并且只有他们设置它。哪种模式取决于情况。 (如果确实需要更改线程之间的所有权,请传递接力棒/令牌。)如果我在这里遇到需要锁,我会仔细检查整体设计。 OTOH,这里的锁是无害的。
    【解决方案4】:

    只使用一个属性

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

    【讨论】:

      【解决方案5】:

      一个简单的方法是对变量使用 get 和 set 函数

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

      【讨论】:

        【解决方案6】:

        你可以使用泛型类:

        class Wrapped<T>  {
            private T _value;
        
            public Action ValueChanged;
        
            public T Value
            {
                get => _value;
        
                set
                {
                    if ( _value != value )
                    {
                        _value = value;
                        OnValueChanged();
                    }
                }
            }
        
            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!
        

        【讨论】:

        • 我个人不喜欢您在更改值之前如何调用OnValueChanged();。似乎是会破坏您的代码的编程错误
        • 在引发更改事件之前,我会亲自检查一下新值是否与旧值不同
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-09
        • 1970-01-01
        • 2012-06-30
        • 1970-01-01
        • 1970-01-01
        • 2019-05-28
        相关资源
        最近更新 更多