7

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?

4
  • possible duplicate of Get name of property as a string Commented Jun 14, 2013 at 7:12
  • @nawfal this is not a duplicate. this question is about "compile time" Commented Jul 18, 2017 at 8:38
  • @symbiont both are about static property names, yes compiler should catch at compile time. Commented Jul 18, 2017 at 8:49
  • @nawfal no. static and compile time are not the same Commented Jul 18, 2017 at 15:59

3 Answers 3

9

In C# 6.0, you can use the nameof() keyword:

And then you write:

var props = new string[]
{
    nameof(Foo.Bar),
    nameof(Foo.Quux),
     ...
};

Everything is done at compile time using this keyword, so it's much better than using lambda expression with code that digs the name of your symbol at runtime. It's better on a performance point of view, and it also works with switch() statements:

switch(e.PropertyName)
{
    case nameof(Foo.Bar):
        break;
}

Using lambda expression or magic get functions, you can't use the switch() statement because the switch() statement requires to use string literals. Since nameof() keywords are converted to string literals at compile time, it works.

Sign up to request clarification or add additional context in comments.

1 Comment

I like this solution
8

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.

4 Comments

@Jonathan: The following usages would be possible, too: (1) Magic_GetName(() => new Foo().Bar) (2) Magic_GetName<Foo>(x => x.Bar)
@Jonathan: I changed my answer to use the second syntax. I think it's a bit cleaner.
My friend pointed out that this is a well-known problem in implementing INotifyPropertyChanged: bing.com/search?q=property+changed+string+not+hard+coded
@Jonathan: Indeed, it was until .NET 4.5. Now you can just use the CallerMemberName attribute: danrigby.com/2012/04/01/… . But that attribute is not applicable in your situation.
4

Much better way of doing that is

GetPropertyName<MemoryDevice>(x => x.DeviceLocator)

public static string GetPropertyName<TClass>(
        Expression<Func<TClass,object>> propertyExpression)
    {
        var body = propertyExpression.ToString();
        body = body.Substring(body.IndexOf(".")+1);
        return body;
    }

another way to do that at runtime is

public static string GetName<TClass>(
    Expression<Func<TClass, object>> propertyExpression)
{
    var body = propertyExpression.Body as UnaryExpression;
    var memberExpression = body.Operand as MemberExpression;
    var propertyInfo = memberExpression.Member as PropertyInfo;

    return propertyInfo.Name;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.