Search code examples
c#inotifypropertychangedref

Why does the SetProperty() method of INotifyPropertyChanged need a ref argument?


Considering the implementation of INotifyPropertyChanged usually looks like this :

    public class Observable : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
        {
            if (Equals(storage, value))
            {
                return;
            }

            storage = value;
            OnPropertyChanged(propertyName);
        }

        protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

Why does the SetPropertymethod need a ref argument ? There's hardly any chance that anything else than a class field gets passed to the method, so it should alway be a reference type anyway ?

Note : I ask this question because I want to use this method with items enumerated through a foreach loop, which doesn't work with ref keyword.


Solution

  • The intention is that you pass a field by reference, as well as the new value.

    If this weren't a ref parameter, you'd be passing the value of the field... at which point this statement:

    storage = value;
    

    ... would be pointless. That would change the value of the parameter, but wouldn't modify the field at all.

    Here's a complete example to demonstrate the difference:

    using System;
    
    class Program
    {
        static string field;
    
        static void Main()
        {
            field = "initial value";
            Console.WriteLine($"Before Modify1: {field}");
    
            Modify1(field, "new value for Modify1");
            Console.WriteLine($"After Modify1: {field}");
    
            Modify2(ref field, "new value for Modify2");
            Console.WriteLine($"After Modify2: {field}");
        }
    
        static void Modify1(string storage, string value)
        {
            // This only changes the parameter
            storage = value; 
        }
    
        static void Modify2(ref string storage, string value)
        {
            // This changes the variable that's been passed by reference,
            // e.g. a field
            storage = value;
        }        
    }
    

    Output:

    Before Modify1: initial value
    After Modify1: initial value
    After Modify2: new value for Modify2
    

    As you can see, Modify1 (without ref) didn't modify the field at all, whereas Modify2 did.