105

I'm currently creating an application in C# using Visual Studio. I want to create some code so that when a variable has a value of 1 then a certain piece of code is carried out. I know that I can use an if statement but the problem is that the value will be changed in an asynchronous process so technically the if statement could be ignored before the value has changed.

Is it possible to create an event handler so that when the variable value changes an event is triggered? If so, how can I do this?

It is completely possible that I could have misunderstood how an if statement works! Any help would be much appreciated.

Heinrich Ulbricht
  • 9,107
  • 3
  • 45
  • 70
James Mundy
  • 3,701
  • 5
  • 31
  • 54
  • 1
    Just to be clear, observing a variable's change is only possible for a variable you own (or which is already IObservable/INotifyPropertyChanged/Event-related). You can't observe a system variable change if it wasn't designed to be observed. – Cœur Jul 11 '14 at 09:39

6 Answers6

131

Seems to me like you want to create a property.

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

private int _myProperty;

This allows you to run some code any time the property value changes. You could raise an event here, if you wanted.

Jonathan Wood
  • 59,750
  • 65
  • 229
  • 380
70

You can use a property setter to raise an event whenever the value of a field is going to change.

You can have your own EventHandler delegate or you can use the famous System.EventHandler delegate.

Usually there's a pattern for this:

  1. Define a public event with an event handler delegate (that has an argument of type EventArgs).
  2. Define a protected virtual method called OnXXXXX (OnMyPropertyValueChanged for example). In this method you should check if the event handler delegate is null and if not you can call it (it means that there are one or more methods attached to the event delegation).
  3. Call this protected method whenever you want to notify subscribers that something has changed.

Here's an example

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();
    }
 }

The advantage of this approach is that you let any other classes that want to inherit from your class to change the behavior if necessary.

If you want to catch an event in a different thread that it's being raised you must be careful not to change the state of objects that are defined in another thread which will cause a cross thread exception to be thrown. To avoid this you can either use an Invoke method on the object that you want to change its state to make sure that the change is happening in the same thread that the event has been raised or in case that you are dealing with a Windows Form you can use a BackgourndWorker to do things in a parallel thread nice and easy.

akappel
  • 209
  • 1
  • 3
  • 15
Beatles1692
  • 4,814
  • 27
  • 60
  • 3
    One of the best explanations on the entire Web. I think I'm finally understanding Custom Event Handling. Thankful for this post. –  Apr 20 '18 at 11:17
44

The .NET framework actually provides an interface that you can use for notifying subscribers when a property has changed: System.ComponentModel.INotifyPropertyChanged. This interface has one event PropertyChanged. Its usually used in WPF for binding but I have found it useful in business layers as a way to standardize property change notification.

In terms of thread safety I would put a lock under in the setter so that you don't run into any race conditions.

Here are my thoughts in code :) :

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
        }
    }
}

Hope this is helpful :)

Daniel Sandberg
  • 551
  • 3
  • 2
  • 6
    +1 for the inclusion of the lock that the other answers omit. – ctacke Apr 30 '11 at 23:35
  • 1
    What is the use of the object _lock ? – Lode Vlaeminck Jan 27 '15 at 10:05
  • 2
    @LodeVlaeminck it prevents changing the value of the property while the event is being processed. – David Suarez Apr 15 '16 at 07:30
  • IMHO, this is an odd place for a lock. [Unless the lock is also used elsewhere, which is a different situation.] If two different threads are in a race condition to set a shared property, then the "final" state of the property is not deterministic. Instead use some pattern where one thread "owns" the property, and only they set it. WHICH pattern depends on the situation. (If really need to change ownership between threads, pass a baton/token.) If I encountered a need for a lock here, I would carefully examine the overall design. OTOH, a lock here is harmless. – ToolmakerSteve Jul 11 '19 at 21:35
13

just use a property

int  _theVariable;
public int TheVariable{
  get{return _theVariable;}
  set{
    _theVariable = value; 
    if ( _theVariable == 1){
      //Do stuff here.
    }
  }
}
Russell Troywest
  • 8,305
  • 2
  • 31
  • 38
1

you can use generic class:

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() ;
}

and will be able to do the following:

var i = new Wrapped<int>();

i.ValueChanged += () => { Console.WriteLine("changed!"); };

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

Console.ReadKey();

result:

changed!
changed!
changed!
changed!
changed!
changed!
changed!
Andrew
  • 5,048
  • 4
  • 35
  • 58
  • 1
    I personally don't like how you call `OnValueChanged();` before changing the value. Seems like a programming error that will break your code – Zun Feb 24 '21 at 13:03
  • 2
    I would personally do a check to see if the new value is different to the old one before raising the change event – michasaurus May 15 '21 at 05:48
1

A simple method involves using the get and set functions on the variable


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