Jonathan Jonathan - 1 month ago 10
C# Question

Getting property names at compile time

I have a class with some properties:

class Foo
{
public int Bar { get; set; }
public string Baz { get; set; }
public bool Quux { get; set; }
(...)
}


For use in some storage API, I need to specify a subset of these properties, by name as strings:

var props = new string[]
{
"Bar",
// Don't want this one... "Baz",
"Quux",
...
};


This works, but is unsafe - if I mistype "Quux", I won't get a compilation error, just (hopefully) a runetime error. I tried reflection -
typeof(Foo).GetProperties("Bar")
- but that would also fail only in runtime.

Ideally, I'd like to do something like:

var props = new string[]
{
Magic_GetName(Foo.Bar),
// Don't want this one... Foo.Baz,
Magic_GetName(Foo.Quux),
...
};


How can I achieve that?

Answer

You can use expressions for this. The usage would look like this:

Magic_GetName<Foo>(x => x.Bar)

The implementation of Magic_GetName would look like this:

public static string Magic_GetName<TClass>(
    Expression<Func<TClass, object>> propertyExpression)
{
    propertyExpression.Dump();
    var body = propertyExpression.Body as UnaryExpression;
    if (body == null)
    {
        throw new ArgumentException(
            string.Format(
                CultureInfo.InvariantCulture, 
                "The body of the 'propertyExpression' should be an " +
                "unary expression, but it is a {0}", 
                propertyExpression.Body.GetType()));
    }

    var memberExpression = body.Operand as MemberExpression;
    if (memberExpression == null)
    {
        throw new ArgumentException(
            string.Format(
                CultureInfo.InvariantCulture, 
                "The operand of the body of 'propertyExpression' should " +
                "be a member expression, but it is a {0}", 
                propertyExpression.Body.GetType()));
    }
    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (propertyInfo == null)
    {
        throw new ArgumentException(
            string.Format(
                CultureInfo.InvariantCulture, 
                "The member used in the expression should be a property, " +
                "but it is a {0}", 
                memberExpression.Member.GetType()));
    }

    return propertyInfo.Name;
}

Update: The title of this question is "Getting property names at compile time".
My answer actually doesn't do that. The method Magic_GetName is executed at runtime and as such has a performance impact.

The .NET 4.5 way using the CallerMemberName attribute on the other hand is really a compile time feature and as such doesn't have a runtime impact. However, as I already said in the comments, it is not applicable in the given scenario.