24

The answer to this question has been edited to say that in C# 6.0, INotifyPropertyChanged can be implemented with the following OnPropertyChanged procedure:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

However, it isn't clear from that answer what the corresponding property definition should be. What does a complete implementation of INotifyPropertyChanged look like in C# 6.0 when this construction is used?

Community
  • 1
  • 1
T.C.
  • 279
  • 1
  • 2
  • 6
  • 1
    The other question / answer already contained all the bits... Each set would just be `set { SetField(ref name, value); }`. The `SetField` method was shown in full. – Marc Gravell Feb 24 '16 at 19:04
  • 2
    @MarcGravell, Yes, but it wasn't clear to me whether the C#5 and C#6 additions were meant to augment or supersede the SetField bit, and I can't request clarification on that question, so I had to ask a new question. I'm glad I did because seeing the entire class written out removes all ambiguity and makes it very easy to understand. – T.C. Feb 24 '16 at 21:21
  • Actually, that's C# 5. – BrainSlugs83 May 03 '16 at 19:37

3 Answers3

34

After incorporating the various changes, the code will look like this. I've highlighted with comments the parts that changed and how each one helps

public class Data : INotifyPropertyChanged
{ 
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        //C# 6 null-safe operator. No need to check for event listeners
        //If there are no listeners, this will be a noop
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // C# 5 - CallMemberName means we don't need to pass the property's name
    protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) 
            return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    private string name;
    public string Name
    {
        get { return name; }
        //C# 5 no need to pass the property name anymore
        set { SetField(ref name, value); }
    }
}
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 1
    What is result value of SetField used for? – dadhi Feb 23 '16 at 17:09
  • It's true if a replacement was actually made and the event fired. It isn't used in this snippet – Panagiotis Kanavos Feb 23 '16 at 17:16
  • If the class contains SetField, then what is the purpose of OnPropertyChanged? Is it expected that some properties will call OnPropertyChanged directly, bypassing SetField? Or is OnPropertyChanged there to satisfy some now-obsolete convention? – T.C. Feb 23 '16 at 17:19
  • @PanagiotisKanavos, since the question is about `C#-6`, could you incorporate `nameof`? – Paulo Morgado Feb 24 '16 at 00:04
  • @PauloMorgado the OP asked how some other answer's code would look after all the updates. I answered simply because I realized that it really was hard to incorporate the changes if you didn't already know how all the parts worked. If you check the comments in the original answer, you'll see that there are performance considerations as well, which is why lambdas aren't used – Panagiotis Kanavos Feb 24 '16 at 08:17
  • Downvoter, please read what was asked first, then explain the *relevant* objection. The questions was "how would the code look after all the updates"? If you think – Panagiotis Kanavos Feb 24 '16 at 08:18
  • @T.C shouldn't you be asking marc_s in the original question for that? It is *not* some old convention though, it's simple separation of concerns. `OnPropertyChanged` makes *raising the event* easier and nothing more. Without it you'd have to spread the code that creates the EventArg parameter, and raises the event everywhere. Not nice. `SetField` does something completely different - it encapsulates setting a field and notifying. – Panagiotis Kanavos Feb 24 '16 at 08:26
  • @PauloMorgado where would `nameof` help? Although that should be a comment to marc_s. Will ping him to update his answer and answer the comments – Panagiotis Kanavos Feb 24 '16 at 08:47
  • @PanagiotisKanavos, the usual example is having FirstName, LastName and FullName properties where the FullName property is a combination of the other two and, thus, any change to the first two should trigger a notification change of the last one. – Paulo Morgado Feb 24 '16 at 12:00
  • @panagiotis-kanavos, the reason I asked about OnPropertyChanged is because Resharper complains about the structure you proposed with the warning "Explicit argument passed to parameter with caller info attribute". I know Resharper isn't always correct about best practice, but I figured I should ask anyway. In any case, I find your justification for OnPropertyChanged compelling. Furthermore, I can now think of cases where it is necessary, such as cases where SetField must be bypassed because, for instance, the type in question doesn't have an equality comparer. – T.C. Feb 24 '16 at 18:21
  • 1
    @panagiotis-kanavos: Regarding your suggestion that I communicate directly with the contributors to the other question, I'll go one step further and suggest that I should have posted my initial question as a comment under the answer there. However, the StackOverflow rules don't me to do either of those things, so this is the best I can do. – T.C. Feb 24 '16 at 18:21
  • 1
    Why SetField returns true/false? – Francesco Bonizzi Oct 22 '16 at 11:01
  • [This post](http://danrigby.com/2012/04/01/inotifypropertychanged-the-net-4-5-way-revisited/) suggests that this could be simplified even further by omitting `EqualityComparer.Default` (and just using Object.Equals). Would you agree? – kmote Apr 25 '17 at 21:33
  • @kmote not at all. Object.Equals only compares the *references*, it doesn't check equality as defined by the *type*. Why raise a change event if the previous and new values are considered equal? Using `Object.Equals` may be suitable for that authors particular case but it's not a generic solution – Panagiotis Kanavos Apr 26 '17 at 07:29
  • 2
    In c#6 you can also use lambda-like-syntax for getters and setters: `get => name; set => SetField(ref name, value);` – mmix May 23 '17 at 21:19
15

I use the same logic in my project. I have a base class for all view models in my app:

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Every view model inherits from this class. Now, in the setter of each property I just need to call OnPropertyChanged().

public class EveryViewModel : PropertyChangedBase
{
    private bool initialized;
    public bool Initialized
    {
        get
        {
            return initialized;
        }
        set
        {
            if (initialized != value)
            {
                initialized = value;
                OnPropertyChanged();
            }
        }
    }

Why does it work?

[CallerMemberName] is automatically populated by the compiler with the name of the member who calls this function. When we call OnPropertyChanged from Initialized, the compiler puts nameof(Initialized) as the parameter to OnPropertyChanged

Another important detail to keep in mind

The framework requires that PropertyChanged and all properties that you're binding to are public.

Amadeusz Wieczorek
  • 3,139
  • 2
  • 28
  • 32
  • 1
    This could be a good answer to the original question, but a bad answer here. The OP asked how some existing code would look after multiple updates. At best, this should be a comment here – Panagiotis Kanavos Feb 24 '16 at 08:16
  • 1
    Besides, you miss the point of the original answer - *instead* of spreading hard-coded value checks and notifications calls everywhere, can you encapsulate all this in a generic way that *allows* custom equality comparisons ? – Panagiotis Kanavos Feb 24 '16 at 08:28
  • 1
    I disagree with the criticisms. Looks great to me. There are things you can do with comparisons, but... meh – Marc Gravell Feb 24 '16 at 19:02
  • I guess I emphasized on the code sample too much, and I should have emphasized "Why does it work?". Everyone, focus on "Why does it work?" – Amadeusz Wieczorek Feb 24 '16 at 19:21
5

I know this question is old, but here is my implementation

Bindable uses a dictionary as a property store. It's easy enough to add the necessary overloads for a subclass to manage its own backing field using ref parameters.

  • No magic string
  • No reflection
  • Can be improved to suppress the default dictionary lookup

The code:

    public class Bindable : INotifyPropertyChanged
    {
        private Dictionary<string, object> _properties = new Dictionary<string, object>();

        /// <summary>
        /// Gets the value of a property
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        protected T Get<T>([CallerMemberName] string name = null)
        {
            object value = null;
            if (_properties.TryGetValue(name, out value))
                return value == null ? default(T) : (T)value;
            return default(T);
        }

        /// <summary>
        /// Sets the value of a property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="name"></param>
        protected void Set<T>(T value, [CallerMemberName] string name = null)
        {
            if (Equals(value, Get<T>(name)))
                return;
            _properties[name] = value;
            OnPropertyChanged(name);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

used like this

public class Item : Bindable
{
     public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
}
Aaron. S
  • 467
  • 4
  • 12
  • There are 2 problems with this approach. Firstly watch out for boxing\unboxing of value types and secondly it loses type safety. A caller could call Set("myProp") but Get("myProp") which would cause an invalid cast exception. So it is down to the caller to ensure it is called correctly. Granted it's fairly safe to assume that a caller won't do that but still as a general rule you'd want to design that possibility out – 1adam12 Oct 05 '17 at 22:03
  • @1adma12 boxing\unboxing? loses type safety? invalid cast exception? could you explain further those three? I use this code in many places and any chance to improve it would be great! – Aaron. S Oct 06 '17 at 20:21
  • Info on boxing\unboxing in C# is available and the type safety issue is that the compiler won't stop you "Get"-ing an value of a different type to the one you original "Set"). Now arguably these are theoretical issues and if you are happy with the trade-offs there's not need to change anything. Indeed WPF's dependency properties work in a similar way to yours and they have accepted similar trade-offs. My point was more to explain why this isn't the generally accepted pattern for INotifyPropertyChanged. You will typically see properties backed with private fields. – 1adam12 Oct 17 '17 at 10:33
  • More on boxing/unboxing: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing – 1adam12 Oct 17 '17 at 10:38
  • @1adma12 I see now, if Get and Set I would get an invalid cast exception. any thoughts on how I would handle or prevent this? – Aaron. S Oct 17 '17 at 17:46
  • There isn't much you can do about. As soon as you cast to Object you loose type safety by definition. Just accept the trade-off - the convenience of not having to create individual backing fields vs. the (likely imperceptible) performance penalty of using an Object dictionary & the potential for a run time exception. If you are satisfied that the benefit outweighs the drawbacks then stick with it. – 1adam12 Oct 18 '17 at 16:56
  • @Aaron.S I am personally a big fan of this approach. Thank you for your innovative answer. Also something to note about the type safety issue. I do not see it as an issue because you will get compiler errors if your Get or Set generic types differ from what is expected by the property type - which enforces some type safety when you compile your code. – Jason Loki Smith Apr 09 '18 at 08:28
  • 1
    @JasonLokiSmith agreed! I couldn't figure out the type safety issue or even how it could be one. thanks for the confirmation on that! – Aaron. S Apr 10 '18 at 16:22