如何获得强类型给定类型的 属性 名称?

How to get a property name of a given type strongly typed?

我希望能够使用强类型语法检索 属性 类型的名称。 我已经有一个函数来获取实例的 属性 名称:

public static string PropertyName<T, TReturn>(this T obj, Expression<Func<T, TReturn>> property) where T : class 
{
    MemberExpression body = (MemberExpression) property.Body;
    if (body == null) throw new ArgumentException("The provided expression did not point to a property.");       
    return body.Member.Name;
}

可以这样调用:

Car car = new Car();
car.PropertyName(x => x.Wheels) //returns "Wheels"

我正在尝试创建另一个可以支持以下功能的函数:

Type t = Typeof(Car);
t.PropertyName(x => x.Wheels) //should return "Wheels"

或者只是(更好!):

Car.PropertyName(x => x.Wheels)

我该怎么做?

您可以重写您的方法以在不创建实例的情况下使用它:

var prop = ReflectionHelper.PropertyName<Car>(x => x.Wheels);

因为你没有在里面使用 obj 因为你不需要它:

public static class ReflectionHelper
{
    public static string PropertyName<T>(Expression<Func<T, object>> property) where T : class 
    {
        MemberExpression body = (MemberExpression)property.Body;
        return body.Member.Name;
    }
}

请注意,return 类型不必是强类型,它可以只是 object

@abatishchev 示例仅在 Wheels 是引用类型时才有效。

如果你有以下情况

public class Car
{
   public int ID;
}

然后你尝试调用这个

var prop = ReflectionHelper.PropertyName<Car>(x => x.ID);

您将得到以下异常

InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.UnaryExpression' to type 'System.Linq.Expressions.MemberExpression'.

我认为这与将值类型传递给表达式这一事实有关,因此它必须装箱到一个对象中。如果传递引用类型,则不需要将其装箱到对象。

你可以做的是:

var prop = ReflectionHelper.PropertyName((Car x) => x.ID);

public static class ReflectionHelper
{
    public static string PropertyName<T, P>(Expression<Func<T, P>> property) 
        where T : class 
    {
        MemberExpression body = (MemberExpression)property.Body;
        return body.Member.Name;
    }
}

在 C# 6 及更高版本中,using static 的语法优势可以使此类方法的用法更具可读性。它还将为您提供早期绑定/编译时检查,因此 CodeLens 将向您展示您的 属性 的用法,这些用法曾被引用为字符串文字(就像在周围浮动的许多数据绑定代码中一样)。这使得重构 变得更容易 当你不得不重命名属性时。

此处的代码基于@abatishchev 的回答,因为在这种情况下(您没有相关类型的实例),使用扩展方法会使代码 更多 冗长,不少。

PropertyName 方法中还有一个额外的行来处理@pedro-faustino 关于转换 UnaryExpression 的异常的观点。 (如果该方法在 propertyExpression 参数中有第二个类型参数,则不需要 return 类型,例如 Expression<Func<T, TMember>>)。

也就是说,这是一个示例,说明如何使用这种策略以早期绑定的方式连接数据绑定:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using BusinessObjects.Vehicles;
using static MyCoolHelperMethods;

namespace Helpers
{
    public static class MyCoolHelperMethods 
    {
        public static string PropertyName<T>(Expression<Func<T, object>> propertyExpression) where T : class
        {
        var memberExpression = propertyExpression.Body as MemberExpression 
                ?? (propertyExpression.Body as UnaryExpression)?.Operand as MemberExpression;
            return memberExpression?.Member.Name;
        }
    }
}

namespace Application
{
    using System.Windows.Forms;

    public class Car
    {
        public string Make {get; set;}
        
        public string Model {get; set;}
    }

    // imagine this is a form with textboxes for car Make and Model:
    public partial class MyWindowsForm
    {
        public MyWindowsForm()
        {
            var car = new Car();
            
            this.txtCarMake.DataBindings.Add(new Binding(
                propertyName: PropertyName<TextBox>(x => x.Text),
                dataSource: car, 
                dataMember: PropertyName<Car>(x => x.Make),
                // formattingEnabled is needed to avoid invalid cast
                // exceptions assigning the object property to the control:
                formattingEnabled: true));
            
            this.txtCarModel.DataBindings.Add(new Binding(
                propertyName: PropertyName<TextBox>(x => x.Text),
                dataSource: car, 
                dataMember: PropertyName<Car>(x => x.Model),
                // formattingEnabled is needed to avoid invalid cast
                // exceptions assigning the object property to the control:
                formattingEnabled: true));
        }
    }
}