views:

822

answers:

6

Microsoft should have implemented something snappy for INotifyPropertyChanged, like in the automatic properties, just specify {get;set;notify;} I think it makes a lot of sense to do it. Or are there any complications to do it?

Can we ourselves implement something like 'notify' in our properties. Is there a graceful solution for implementing INotifyPropertyChanged in your class or the only way to do it is by raising the PropertyChanged event in each property.

If not can we write something to autogenerate the peice of code to raise PropertyChanged event?

+1  A: 

I haven't actually had a chance to try this myself yet, but next time I'm setting up a project with a big requirement for INotifyPropertyChanged I'm intending on writing a Postsharp attribute that will inject the code at compile time. Something 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 if this will work in practice and I need to sit down and try it out, but I don't see why not. I may need to make it accept some parameters for situations where more than one OnPropertyChanged needs to be triggered (if, for example, I had a FullName property in the class above)

Currently I'm using a custom template in Resharper, but even with that I'm getting fed up of all my properties being so long.


Ah, a quick Google search (which I should have done before I wrote this) shows that at least one person has done something like this before here. Not exactly what I had in mind, but close enough to show that the theory is good.

Martin Harris
+15  A: 

Without using something like postsharp, the minimal version I use uses something like:

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

Each property is then just something like:

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

which isn't huge; it can also be used as a base-class if you want. The bool return from SetField tells you if it was a no-op, in case you want to apply other logic.

Marc Gravell
Nice trick Marc ! I suggested an improvement to use a lambda expression instead of the property name, see my answer
Thomas Levesque
DevXpress Xpo does it this way.
Anton Tykhyy
cool. Thank you.
P.K
@Thomas - the lambda is all well and good, but it adds a lot of overhead for something that is actually very simple. A handy trick, but I'm not sure it is always practical.
Marc Gravell
@Marc - Yes, it can probably degrade performance... However I really like the fact that it's checked at compile time, and is correctly refactored by the "Rename" command
Thomas Levesque
+6  A: 

I really like Marc's solution, but I think it can be slightly improved to avoid using a "magic string" (which doesn't support refactoring). Instead of using the property name as a string, it's easy to 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, it will do the trick :

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

BTW, this was inspired by this blog post

Thomas Levesque
For info, since `Expression<T>` *inherits* from `LambdaExpression`, the first test is unnecessary - or should just be an `ArgumentNullException` check.
Marc Gravell
Oops, good point... I never took a close look at the Linq expressions type hierarchy ;)
Thomas Levesque
fixed
Thomas Levesque
+2  A: 

Talk about massive overengineering. This is significantly more complex than just doing it the right way [0] and gives little to no benefit. If your IDE supports code snippets[1] (Visual Studio/MonoDevelop do) then you can make implementing this rediculously simple. All you'd actually have to type is the type of the property and the property name. The extra three lines of code will be autogenerated.

[0] http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx

[1] http://msdn.microsoft.com/en-us/library/ms165392%28VS.80%29.aspx

Alan
In order to stay away from magic strings, you can also use the code from this blog post: http://blog.m.jedynak.pl/2009/02/static-typed-propety-names.html
Brett Veenstra
+3  A: 

A very AOP-like approach is to inject the INotifyPropertyChanged stuff onto an already instantiated object on the fly. You can do this with something like Castle DynamicProxy. Here is an article that explains the technique:

Adding INotifyPropertyChanged to an existing object

HokieMike
A: 

Other things you may want to consider when implementing these sorts of properties is the fact that the INotifyPropertyChang *ed *ing both use event argument classes.

If you have a large number of properties that are being set then the number of event argument class instances can be huge, you should consider caching them as they are one of the areas that a string explosion can occur.

Take a look at this implementation and explanation of why it was conceived.

Josh Smiths Blog

Peter