7

Most of the time when we use MVVM we use the INotifyPropertyChanged interface to provide the notification to the bindings, and the general implementation looks like this:

public class MyClass : INotifyPropertyChanged
{
    // properties implementation with RaisePropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

This works fine for me whenever I read the code from the experts - they wrote similar code:

public class MyClass : INotifyPropertyChanged
{
    // properties implementation with RaisePropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string propertyName)
    {
        var tempchanged = PropertyChanged;
        if (tempchanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

I would like to know what is the exact reason behind creating a temporary object for the PropertyChanged event.

Is it only a good practice or are there any other benefits associated with it?

I have found the answer with Jon's answer and the explained example at:

Understanding C#: Raising events using a temporary variable

Here is the sample code to understand this:

using System;
using System.Collections.Generic;
using System.Threading;

class Plane
{
     public event EventHandler Land;

     protected void OnLand()
     {
          if (null != Land)
          {
               Land(this, null);
           }
      }

     public void LandThePlane()
     {
          OnLand();
      }
}

class Program
{
     static void Main(string[] args)
     {
          Plane p = new Plane();
          ParameterizedThreadStart start = new ParameterizedThreadStart(Run);
          Thread thread = new Thread(start);
          thread.Start(p);

          while (true)
          {
               p.LandThePlane();
           }
      }

     static void Run(object o)
     {
          Plane p = o as Plane;
          while (p != null)
          {
               p.Land += p_Land;
               p.Land -= p_Land;
           }
      }

     static void p_Land(object sender, EventArgs e)
     {
          return;
      }
}
JSJ
  • 5,653
  • 3
  • 25
  • 32
  • See this article, http://stackoverflow.com/questions/282653/checking-for-null-before-event-dispatching-thread-safe – Bill Zhang Jul 02 '13 at 13:24
  • See Eric Lippert's article on the topic, [Events and Races](http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx) – Brian Jul 02 '13 at 15:43

4 Answers4

26

You're not creating a temporary object. You're using a local variable to avoid a race condition.

In this code:

if (PropertyChanged != null)
{
    PropertyChanged(...);
}

it's possible for PropertyChanged to become null (due to the last subscriber unsubscribing) after the nullity check, which will mean you get a NullReferenceException.

When you use a local variable, you ensure that the reference you check for nullity is the same reference that you use to raise the event - so you won't get the exception. There's still a race condition in that you may end up calling subscribers who have just unsubscribed, but that's unavoidable.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
4

It's to avoid the rare case when the last (or only) event handler is removed from the event between the time you check for null (to see if any event handler is attached) and the time you call the event. If that happens you would get a NullReferenceException.

If you're concerned about memory leaks - don't be - it's just a reference, not a copy of the event handler.

More details can be found here

D Stanley
  • 149,601
  • 11
  • 178
  • 240
1

This is good practise for reasons of thread safety.

In your original code, it is theoretically possible for a separate thread to remove the PropertyChanged handler after the if statement but before the event is raised on the following line. This would cause a NullReferenceException.

The second sample removes this risk.

Dan Puzey
  • 33,626
  • 4
  • 73
  • 96
0

It only makes a difference when dealing with multi-threaded scenarios: When the last event handler is deregistered after the != null check, the actual call might run into a NullReferenceException in code 1.

Code 2, however, doesn't have this problem as Delegates (the concept behind events) are immutable and thus the value of the temporary variable can't change.

However, I would recommend always going with variant 2 as a best practice - this might save you a headache in the future ;-)

Matthias
  • 12,053
  • 4
  • 49
  • 91