7

我有一个具有一些属性的类:

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”,我不会收到编译错误,只是(希望)出现 runetime 错误。我尝试了反射--typeof(Foo).GetProperties("Bar")但这也只会在运行时失败。

理想情况下,我想做类似的事情:

var props = new string[]
{
    Magic_GetName(Foo.Bar),
    // Don't want this one... Foo.Baz,
    Magic_GetName(Foo.Quux),
     ...
};

我怎样才能做到这一点?

4

3 回答 3

9

在 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()关键字在编译时被转换为字符串文字,所以它可以工作。

于 2016-10-12T22:31:25.610 回答
8

您可以为此使用表达式。用法如下所示:

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运行时执行,因此会对性能产生影响。

另一方面,使用CallerMemberName属性的 .NET 4.5 方式实际上是一个编译时功能,因此不会对运行时产生影响。但是,正如我在评论中已经说过的,它不适用于给定的场景。

于 2013-04-10T07:48:33.177 回答
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;
}
于 2013-07-11T13:23:45.460 回答