简体   繁体   中英

Why we should use temprary object to raise event?

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

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.

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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM