在编译时获取属性名

本文关键字:属性 获取 编译 | 更新日期: 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;
}