Click here to Skip to main content
15,895,192 members
Articles / Desktop Programming / MFC

Using Property Name Without Typos

Rate me:
Please Sign up or sign in to vote.
3.79/5 (12 votes)
18 Apr 2017CPOL2 min read 22.1K   12   8
Referring to a property via a string containing its name

In various scenarios, there is a need for referring to a property via a string containing its name. Probably the most common case is when invoking the PropertyChanged event handler:

C#
// Notify that property value has changed.
PropertyChanged(this, new PropertyChangedEventArgs("SomePropertyName"));

Obviously, this is error prone. When it comes to string values, a typo can easily be made. The solution to this problem is to get the property name automatically from the property reference. This is where lambda expressions become handy. () => Property or x => x.Property lets you perceive any faults during compile time or even earlier during design time.

To clarify, I’m not discovering anything new here, nor reinventing the wheel. For several years, the subject has been known, discussed, and implemented. While reviewing a few implementations I stumbled across, I decided to create my own variation, anyway to put together some features scattered around.

Usage

The PropertyName helper class can be used to retrieve the property name of static and non-static properties from object instances and types directly. It is also possible to get property names from nested properties. Some examples:

C#
// Get the property name form object instance
var obj = new SampleObject();
var name1 = PropertyName.For(() => obj.SampleProperty);
Assert.AreEqual("SampleProperty", name1);

// Get the property name form within object instance
var name2 = PropertyName.For(() => this.SampleProperty);
Assert.AreEqual("SampleProperty", name2);

// Get the name of a static property
var name3 = PropertyName.For(() => SampleType.StaticProperty);
Assert.AreEqual("StaticProperty", name3);

// Get the property name from a type
var name4 = PropertyName.For<SampleType>(x => x.SampleProperty);
Assert.AreEqual("SampleProperty", name4);

// Get the name of a nested property from object instance and type
var name5 = PropertyName.For(() => this.SampleProperty.NestedProperty);
var name6 = PropertyName.For<SampleType>(x => x.SampleProperty.NestedProperty);
Assert.AreEqual("SampleProperty.NestedProperty", name5);
Assert.AreEqual("SampleProperty.NestedProperty", name6);

Source Code

Below is the full code of the PropertyName helper class:

C#
/// <summary>
/// Returns the name of the property using lambda expression.
/// </summary>
public sealed class PropertyName
{
    /// <summary>
    /// Error message in case lambda expression does not contain a property.
    /// </summary>
    private const string _ErrorMessage = "Expression '{0}' does not contain a property.";

    /// <summary>
    /// Returns the name of the property using lambda expression.
    /// </summary>
    /// <typeparam name="T">Type containing the property.</typeparam>
    /// <param name="propertyExpression">
    /// Expression containing the property.</param>
    /// <returns>The name of the property.</returns>
    public static string For<T>(Expression<Func<T, 
           object>> propertyExpression)
    {
        if (propertyExpression == null)
        {
            throw new ArgumentNullException("propertyExpression");
        }
        var body = propertyExpression.Body;
        return GetPropertyName(body);
    }

    /// <summary>
    /// Returns the name of the property using lambda expression.
    /// </summary>
    /// <param name="propertyExpression">Expression 
    ///           containing the property.</param>
    /// <returns>The name of the property.</returns>
    public static string For(Expression<Func<object>> propertyExpression)
    {
        if (propertyExpression == null)
        {
            throw new ArgumentNullException("propertyExpression");
        }
        var body = propertyExpression.Body;
        return GetPropertyName(body);
    }

    /// <summary>
    /// Returns the name of the property using lambda expression.
    /// </summary>
    /// <param name="propertyExpression">
    /// Expression containing the property.</param>
    /// <param name="nested">Is it a recurrent invocation?</param>
    /// <returns>The name of the property.</returns>
    private static string GetPropertyName(
            Expression propertyExpression, bool nested = false)
    {
        MemberExpression memberExpression;

        if (propertyExpression is UnaryExpression)
        {
            var unaryExpression = (UnaryExpression)propertyExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = propertyExpression as MemberExpression;
        }

        if (memberExpression == null)
        {
            if (nested) return string.Empty;
            throw new ArgumentException(
              string.Format(_ErrorMessage, propertyExpression),
              "propertyExpression");
        }

        var propertyInfo = memberExpression.Member as PropertyInfo;
        if (propertyInfo == null)
        {
            if (nested) return string.Empty;
            throw new ArgumentException(
              string.Format(_ErrorMessage, propertyExpression),
              "propertyExpression");
        }

        return (memberExpression.Expression != null &&
           memberExpression.Expression.NodeType == ExpressionType.MemberAccess)
             ? GetPropertyName(memberExpression.Expression, true) + 
             propertyInfo.Name : propertyInfo.Name + 
             (nested ? "." : string.Empty);
    }
}

Some Light on the Main Code

Now, let us discuss the body of the GetPropertyName method. The expression containing a property of the reference type is of the MemberExpression type. Nothing unusual here. For a property of value type, it is of UnaryExpression type, however. Its Operand member contains the value of MemberExpression that we are interested in. In other words, an expression containing a value type property is boxed and we need to unbox it.

Since there are other expression types that might accidentally sail in, we cannot explicitly cast an expression to MemberExpression but use as operator and check for nullity.

The propertyInfo block is just for ensuring that we are getting the name of a property. Generally, this part could be dropped and the class called MemberName.

Let's move on to the return statement and recurrence that occurs here. It is designed to get the nested properties, e.g., x => x.SomeProperty.SomeSubProperty.OrEvenMore. The order of property names retrieval is bottom-up. The first chunk returned for the example above is OrEvenMore, then SomeSubProperty and SomeProperty, respectively.

The if (nested) return string.Empty; conditional statement is used to enable () => obj.SomeProperty. The obj chunk fits the return statements conditions and the GetPropertyName method is invoked recurrently. It may seem not to be the neatest solution, but it’s the best I came up with for the moment. Got any hints regarding this issue, please let me know.

References

License

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


Written By
Technical Lead
Poland Poland
Aoi Karasu is a Software Developer with his first programming experience at the age of 7 (FoxBase and Turbo Pascal for DOS).

Karasu has extensive knowledge and experience with many Microsoft technologies and languages, including C++, C#, Xamarin, ASP.NET MVC, WinForms, WPF, .NET Interop, CLI, ActiveX/COM, MFC/ATL/WTL, Windows API, OCS/UCMA/UCC API, reverse engineering (assembly, IL) and lately diving into UWP development.

Comments and Discussions

 
Questionthe 'CallerMemberName added in C# 4.5 makes this unnecessary Pin
BillWoodruff19-Apr-17 20:17
professionalBillWoodruff19-Apr-17 20:17 
AnswerRe: the 'CallerMemberName added in C# 4.5 makes this unnecessary Pin
Aoi Karasu 28-Apr-17 15:46
professional Aoi Karasu 28-Apr-17 15:46 
QuestionElegant Pin
TetheredSun19-Apr-17 2:04
TetheredSun19-Apr-17 2:04 
AnswerRe: Elegant Pin
Aoi Karasu 28-Apr-17 15:50
professional Aoi Karasu 28-Apr-17 15:50 
Questionuse nameof if using c# 6.0 PinPopular
Prabu ram18-Apr-17 18:28
Prabu ram18-Apr-17 18:28 
AnswerRe: use nameof if using c# 6.0 Pin
Aoi Karasu 28-Apr-17 15:42
professional Aoi Karasu 28-Apr-17 15:42 
GeneralRe: use nameof if using c# 6.0 Pin
Prabu ram1-May-17 3:32
Prabu ram1-May-17 3:32 
Oh, sorry! the intention was not to find fault in your post, but just to let others to know there is newer solution if they use C# 6.0 like me, despite you originally published this long long ago Big Grin | :-D
Praburam

QuestionAleardy exists in MVC Pin
Mehran Farshadmehr13-Sep-11 4:02
Mehran Farshadmehr13-Sep-11 4:02 
AnswerRe: Aleardy exists in MVC Pin
Aoi Karasu 13-Sep-11 20:54
professional Aoi Karasu 13-Sep-11 20:54 

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.