我有一个包含一些属性的类:
class Foo
{
public int Bar { get; set; }
public string Baz { get; set; }
public bool Quux { get; set; }
(...)
}
要在某些存储API中使用,我需要指定这些属性的子集,名称为字符串:
var props = new string[]
{
"Bar",
// Don't want this one... "Baz",
"Quux",
...
};
这有效,但是不安全 - 如果我输错“Quux”,我不会得到编译错误,只是(希望)一个符文时间错误。我尝试过反射 - typeof(Foo).GetProperties("Bar")
- 但这也只会在运行时失败。
理想情况下,我想做类似的事情:
var props = new string[]
{
Magic_GetName(Foo.Bar),
// Don't want this one... Foo.Baz,
Magic_GetName(Foo.Quux),
...
};
我怎样才能做到这一点?
答案 0 :(得分:7)
您可以使用表达式。用法如下:
Magic_GetName<Foo>(x => x.Bar)
Magic_GetName
的实现如下所示:
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;
}
<强>更新强>
这个问题的标题是&#34;在编译时间获取属性名称&#34;
我的答案实际上并没有这样做。方法Magic_GetName
在运行时执行,因此会对性能产生影响。
另一方面,使用the CallerMemberName
attribute的.NET 4.5方式实际上是a compile time feature,因此不会产生运行时影响。但是,正如我在评论中已经说过的那样,它不适用于给定的场景。
答案 1 :(得分:7)
在C#6.0中,您可以使用nameof()关键字:
然后你写道:
var props = new string[]
{
nameof(Foo.Bar),
nameof(Foo.Quux),
...
};
所有事情都是在编译时使用这个关键字完成的,所以它比使用lambda表达式和在运行时挖掘符号名称的代码要好得多。从性能的角度来看,它更好,它也适用于switch()
语句:
switch(e.PropertyName)
{
case nameof(Foo.Bar):
break;
}
使用lambda表达式或魔术获取函数,您不能使用switch()
语句,因为switch()
语句需要使用字符串文字。由于nameof()
关键字在编译时转换为字符串文字,因此可以正常工作。
答案 2 :(得分:4)
更好的方法是
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;
}
在运行时这样做的另一种方法是
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;
}