Reputation: 7551
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?
Upvotes: 7
Views: 4505
Reputation: 809
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;
}
Upvotes: 4
Reputation: 5498
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.
Upvotes: 9
Reputation: 174299
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.
Upvotes: 8