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

时间:2015-02-17 19:20:26

标签: c# .net reflection expression-trees

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

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)

我该怎么做?

3 个答案:

答案 0 :(得分:7)

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

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

请注意,返回类型不必是强类型的,它可以只是object

答案 1 :(得分:1)

@abatishchev示例仅在Wheels是引用类型的情况下有效。

如果您具有以下条件

public class Car
{
   public int ID;
}

您尝试称呼它

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

您将收到以下异常

  

InvalidCastException:无法转换类型的对象   键入“ System.Linq.Expressions.UnaryExpression”   '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;
    }
}

答案 2 :(得分:1)

在C#6和更高版本中,using static的句法优势可以使这种方法的用法更具可读性。它还将为您提供早期的绑定/编译时检查,因此CodeLens将向您显示对属性的用法,这些属性曾经被称为字符串文字(例如,在周围浮动的许多数据绑定代码中)。当您不得不重新命名属性时,这使得重构很多更加容易。

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

PropertyName方法中还有一行额外内容,用于处理@ pedro-faustino关于强制转换UnaryExpression的异常的观点。 (如果该方法在propertyExpression参数中有第二个类型参数,则返回类型为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));
        }
    }
}