Is there a better way to implement INotifyPropertyChanged?

Keywords: Attribute Google Lambda

Microsoft should implement some simple functions for INotifyPropertyChanged. For example, in automatic properties, you only need to specify {get; set; notify;} {get; set; notify;} {get; set; notify;}. I think it makes sense to do so. Or do you have any complications?

Can we implement "notification" and other functions in properties? Is there an elegant solution for implementing INotifyPropertyChanged in your class, or the only way is by raising the PropertyChanged event in each property.

If not, can we write something to automatically generate the code that raises the PropertyChanged event?

#1 building

Starting with. Net 4.5, there's finally a simple way.

. Net 4.5 introduces a new caller information attribute.

private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
     // make sure only to call this if the value actually changes

     var handler = PropertyChanged;
     if (handler != null) {
        handler(this, new PropertyChangedEventArgs(caller));
     }
}

It is best to add a comparer to the function.

EqualityComparer<T>.Default.Equals

Here and Here you are More examples

See also Caller information (C and Visual Basic)

#2 building

I haven't actually had a chance to try it on my own, but next time I'm going to create a Very high I intend to write a Postsharp Property that will inject code at compile time. It's like:

[NotifiesChange]
public string FirstName { get; set; }

Will become:

private string _firstName;

public string FirstName
{
   get { return _firstname; }
   set
   {
      if (_firstname != value)
      {
          _firstname = value;
          OnPropertyChanged("FirstName")
      }
   }
}

I'm not sure it works in practice, I need to sit down and try, but I don't know why not. For situations where multiple OnPropertyChanged needs to be triggered, I may need to make it accept certain parameters (for example, if I have the FullName property in the above class)

Currently, I use custom templates in Resharper, but even then, I'm tired of all my properties being too long.

Well, a quick Google search (which I should have done before writing this article) shows that at least one person is Here I did something similar before. Not exactly my original intention, but close enough to prove that the theory is good.

#3 building

Without using postsharp or the like, the minimum version I use is:

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

Then each attribute is like:

    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }

This is not very large; it can also be used as a base class if needed. SetField's bool returns SetField to tell you whether the operation is empty, in case you want to apply other logic.

Even easier to use C 5:

protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
{...}

You can call it this:

set { SetField(ref name, value); }

The compiler will use the Name to automatically add a Name.

C 6.0 makes implementation easier:

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

... now use C 7:

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


private string name;
public string Name
{
    get => name;
    set => SetField(ref name, value);
}

#4 building

I really like Marc's solution, but I think it can be slightly improved to avoid using "magic strings" (refactoring is not supported). Instead of using the property name as a string, make it a lambda expression:

private string name;
public string Name
{
    get { return name; }
    set { SetField(ref name, value, () => Name); }
}

Just add the following methods to Marc's code:

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
    if (selectorExpression == null)
        throw new ArgumentNullException("selectorExpression");
    MemberExpression body = selectorExpression.Body as MemberExpression;
    if (body == null)
        throw new ArgumentException("The body must be a member expression");
    OnPropertyChanged(body.Member.Name);
}

protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(selectorExpression);
    return true;
}

By the way, that's what it takes Blog articles Update URL Inspire

#5 building

View here: http : //dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx

It is written in German, but you can download ViewModelBase.cs. All comments in the CS file are written in English.

Using this viewmodelbase class, you can implement a bindable property similar to the well-known Dependency Properties:

public string SomeProperty
{
    get { return GetValue( () => SomeProperty ); }
    set { SetValue( () => SomeProperty, value ); }
}

Posted by bnmng on Fri, 03 Jan 2020 16:59:39 -0800