如何获取特定属性的PropertyInfo?

93

我想要获取特定属性的 PropertyInfo。我可以使用:

foreach(PropertyInfo p in typeof(MyObject).GetProperties())
{
    if ( p.Name == "MyProperty") { return p }
}

但肯定有一种类似于这样的方式

typeof(MyProperty) as PropertyInfo

有吗?还是我只能使用不安全的类型字符串比较?

谢谢。

5个回答

141

有一种使用lambda/Expression的.NET 3.5方法,不需要使用字符串...

using System;
using System.Linq.Expressions;
using System.Reflection;

class Foo
{
    public string Bar { get; set; }
}
static class Program
{
    static void Main()
    {
        PropertyInfo prop = PropertyHelper<Foo>.GetProperty(x => x.Bar);
    }
}
public static class PropertyHelper<T>
{
    public static PropertyInfo GetProperty<TValue>(
        Expression<Func<T, TValue>> selector)
    {
        Expression body = selector;
        if (body is LambdaExpression)
        {
            body = ((LambdaExpression)body).Body;
        }
        switch (body.NodeType)
        {
            case ExpressionType.MemberAccess:
                return (PropertyInfo)((MemberExpression)body).Member;
            default:
                throw new InvalidOperationException();
        }
    }
}

不错的解决方案,但不幸的是我没有使用.NET3.5。还是点赞! - tenpn
1
在2.0中,Vojislav Stojkovic的回答是你能得到的最接近的答案。 - Marc Gravell
4
为什么在提取“.Body”属性之前要对“body是否为LambdaExpression”进行测试?难道选择器不总是LambdaExpression吗? - tigrou
@tigrou 很可能只是疏忽了,也许是我借用了已经针对Expression起作用的现有代码。 - Marc Gravell
@MarcGravell 这个实现并不是很稳健。在PropertyHelper<Derived>.GetProperty(x => x.BaseProperty);的情况下,您将无法获取正确的属性信息。请参见https://dev59.com/HGw15IYBdhLWcg3wYKmo。 - nawfal

80

你可以使用 C# 6 中的新 nameof() 操作符,它可在 Visual Studio 2015 中使用。更多信息请参见此处

对于您的示例,您将使用:

PropertyInfo result = typeof(MyObject).GetProperty(nameof(MyObject.MyProperty)) ?? throw new Exception();
编译器将会把nameof(MyObject.MyProperty)转换成字符串 "MyProperty",但是你可以享受到能够重构属性名称的好处,而不必记得要改变字符串,因为Visual Studio、ReSharper等工具知道如何重构nameof()的值。

2
如果你的示例以 PropertyInfo result = 开始而不是 var result =,那么它可能会更清晰一些。 - DavidRR
那段代码不会编译通过,除非你在结尾加上 ?? throw new Exception(),因为编译器仍然认为它可能返回 null - Jon Hallin

13
你可以这样做:
typeof(MyObject).GetProperty("MyProperty")

然而,由于C#没有"符号"类型,因此没有任何东西能够帮助你避免使用字符串。顺便问一句,为什么你称它为"不安全的类型"?


40
因为它不是在编译时进行评估的?如果我更改了属性名称或拼写错误的字符串,直到代码运行时才能发现。 - tenpn

2
这可能是最佳方法:
public static class TypeExtensions
    {
        public static PropertyInfo? GetProperty<T, TValue>(this T type, Expression<Func<T, TValue>> selector) where T : class
        {
            Expression expression = selector.Body;

            return expression.NodeType == ExpressionType.MemberAccess ? (PropertyInfo)((MemberExpression)expression).Member : null;
        }
    }

使用方法:

myObject.GetProperty(opt => opt.PropertyName);

1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community
这似乎是一个简单的快速技巧,用法示例: myObj.GetProperty(x=>x.property) - L4marr

1

反射用于运行时类型评估。因此,您的字符串常量无法在编译时进行验证。


6
这就是 OP 想要避免的。不确定这是否回答了问题。 - nawfal
关于编译时和运行时的好处以及OP的原始意图,这是一个很好的观点,但避免硬编码字符串似乎仍然是最清晰的解决方案——避免了可能出现的拼写错误,使重构更容易,并且使代码风格更加整洁。 - Brian Sweeney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接