Click here to Skip to main content
15,881,852 members
Articles / Programming Languages / C#

CallMemberName – An Easier Way To Do INotifyPropertyChanged and More

Rate me:
Please Sign up or sign in to vote.
4.90/5 (9 votes)
27 Aug 2012CPOL2 min read 17.4K   10   10
An easier way to do INotifyPropertyChanged

In WPF, when applying the MVVM (an architectural pattern), we often need to implement the INotifyPropertyChanged on certain classes (ViewModel classes), which means something like this:

C#
public class PersonViewModel : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value) return;
            _name = value;
            NotifyPropertyChanged("Name");
        }
    }

    private void NotifyPropertyChanged(string propertyName)
    {
        var evt = PropertyChanged;
        if (evt != null) evt(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

In case you’re wondering why I copied the PropertyChanged value to the local variable called “evt” and then tested it for null is that you can have race conditions, in general, triggering events (i.e.: you test the attribute value, it is not null and before you trigger it, some other thread sets it to null and bang, NullReferenceException when you trigger it). More details on this CodeProject.

The next step is to pull the NotifyPropertyChanged method and PropertyChanged event into a base class (let’s call it ViewModelBase) and you’ve eliminated redundancy between several ViewModel classes.

The not-so-nice part is having the call to NotifyPropertyChanged stringly-typed. That means that if later you rename (via Visual Studio or ReSharper) the Name property to “FullName” the call will still pass “Name” as the argument.

Some blog posts around the web show how you can use a Func to make it type-safe (refactor safe, etc.).

More or less they’re doing the same thing:

C#
public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(Expression<Func<object>> propertyAccessor)
    {
        var evt = PropertyChanged;
        if (evt == null) return;
        var propertyName = propertyAccessor.GetName();
        evt(this, new PropertyChangedEventArgs(propertyName));
    }
}

public static class Utils
{
    public static string GetName(this LambdaExpression expression)
    {
        MemberExpression memberExpression;
        if (expression.Body is UnaryExpression)
        {
            var unaryExpression = (UnaryExpression)expression.Body;
            memberExpression = (MemberExpression)unaryExpression.Operand;
        }
        else if (expression.Body is MemberExpression)
        {
            memberExpression = (MemberExpression)expression.Body;
        }
        else
        {
            return null;
        }
        return memberExpression.Member.Name;
    }
}

This is definitely nicer, not-redundant and type-safe. It does have the drawback of having some runtime performance penalty associated with the reflection of the expression. You could cache the property name string in a private field but then you’d have to write more code in the ViewModel classes which would… suck. In practice, this performance penalty is negligible so you can just ignore this.

Then came .NET 4.5 and among other improvements a new mechanism has been introduced: CallMemberName.

Historically, some folks tried to get programmatically the name of the caller method by inspecting the StackTrace (for example, using System.Environment.StackTrace) but this is prone to errors since in Release mode, the compiler could eliminate some methods by inlining them and you’ll be screwed. Plus the penalty would be higher than reflecting an expression.

The new mechanism in .NET 4.5 is type-safe, has no runtime performance penalty and it’s more elegant. Here’s how you can use it:

C#
public abstract class ViewModelBase : INotifyPropertyChanged
{
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var deleg = PropertyChanged;
        if (deleg != null)
        {
            deleg(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class PersonViewModel : ViewModelBase
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value) return;
            _name = value;
            NotifyPropertyChanged();
        }
    }
}

I’ve recently built a very small GuidGen utility (which as the name implies generates GUIDs, copies it in the Windows Clipboard and stores a history of past generated GUIDs). You can browse some of the code and check out the project.

Much nicer, isn’t it?

Funny thing, this new mechanism can be used for non-UI tasks. For example, if you have a project that uses an RDBMS and you use stored procedures. Let’s say you have one method in a repository class for each stored procedure, and even more, the method’s name matches the stored procedure’s name:

C#
public VerificationResult VerifyUser(VerificationData verificationData)
{
    if (EmailValidator.IsEmailInvalid(verificationData.EmailAddress))
        throw new FormatException("emailAddress");

    var result = CreateNewCommand("VerifyUser").GetEnumResult<VerificationFailReason>(
        CreateEmailAddressParameter(verificationData.EmailAddress),
        CreateUniqueIdentifierParam("@VerificationCode", verificationData.VerificationCode));

    return new VerificationResult(result);
}

Observe on line 5 how the call to CreateNewCommand passes a string which matches the current method’s name. This can also be simplified (and become refactor-safe) using the new CallMemberName mechanism.

So you can’t really say that CallMemberName is useful only for UI tasks. :)

This article was originally posted at http://blog.andrei.rinea.ro?p=251

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) IBM, Business Analytics
Romania Romania
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
lyphoon24-Sep-12 23:33
lyphoon24-Sep-12 23:33 
GeneralMy vote of 5 Pin
-= Micronn =-4-Sep-12 1:07
-= Micronn =-4-Sep-12 1:07 
GeneralMy vote of 3 Pin
wvd_vegt27-Aug-12 22:43
professionalwvd_vegt27-Aug-12 22:43 
GeneralRe: My vote of 3 Pin
Andrei Ion Rînea27-Aug-12 23:34
Andrei Ion Rînea27-Aug-12 23:34 
GeneralRe: My vote of 3 Pin
wvd_vegt28-Aug-12 0:39
professionalwvd_vegt28-Aug-12 0:39 
SuggestionCaller Information in VS2012 Pin
kornman0027-Aug-12 11:05
kornman0027-Aug-12 11:05 
You can have the compiler insert the caller's name (literal) using an attribute on the parameter in VS2012: http://msdn.microsoft.com/en-us/library/hh534540.aspx[^]

edit: Also, there's an edge case in getting a property's name with LINQ Expressions, though I don't think it really applies here (but it should be noted): ReflectionHelper[^]
GeneralRe: Caller Information in VS2012 Pin
Andrei Ion Rînea27-Aug-12 23:32
Andrei Ion Rînea27-Aug-12 23:32 
GeneralRe: Caller Information in VS2012 Pin
kornman001-Sep-12 19:34
kornman001-Sep-12 19:34 
GeneralMy vote of 5 Pin
Christian Amado27-Aug-12 9:18
professionalChristian Amado27-Aug-12 9:18 
GeneralRe: My vote of 5 Pin
Andrei Ion Rînea27-Aug-12 23:35
Andrei Ion Rînea27-Aug-12 23:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.