在编译时获取属性名
本文关键字:属性 获取 编译 | 更新日期: 2023-09-27 18:08:18
我有一个类的一些属性:
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),
...
};
我怎么才能做到呢?
在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表达式或魔术get函数,您不能使用switch()
语句,因为switch()
语句需要使用字符串字面值。由于nameof()
关键字在编译时被转换为字符串字面值,因此它可以工作。
可以使用表达式。用法如下:
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;
}
更新:这个问题的标题是"在编译时间获取属性名称"。
我的答案并不是这样的。方法Magic_GetName
在运行时执行,因此对性能有影响。
另一方面,. net 4.5使用CallerMemberName
属性的方式实际上是一个编译时特性,因此不会对运行时产生影响。但是,正如我在评论中已经说过的,它不适用于给定的场景。
更好的方法是
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;
}