在编译时获取属性名称

时间:2013-04-10 07:41:35

标签: c#

我有一个包含一些属性的类:

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),
     ...
};

我怎样才能做到这一点?

3 个答案:

答案 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;
}