【问题标题】:How to get a property name of a given type strongly typed? [duplicate]如何获取给定类型的强类型属性名称? [复制]
【发布时间】:2015-02-17 19:20:26
【问题描述】:

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

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)

我该怎么办?

【问题讨论】:

  • 泛型用于编译时已知类型。 System.Type 不能有类似的方法。想象一个案例:Type t = Type.GetType("Hi there"); t.PropertyName(x =&gt; ???);
  • 顺便说一句:在 Roslyn 或 C# 6 中,您只需使用 nameof()
  • 我的问题是,你为什么需要这样的结构?让这样的函数返回一个字符串值有什么用?

标签: c# .net reflection expression-trees


【解决方案1】:

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

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

【讨论】:

  • 您的示例无法编译,因为您在PropertyName&lt;Car&gt;() 的调用中忘记了第二个类型TReturn... 而写ReflectionHelper.PropertyName&lt;Car,WhellType&gt;(x =&gt; x.Wheels); 真的很无聊,因为您不想明确地写每次您只想提取其名称时的属性类型...
  • @Sharped:谢谢,已删除
  • @abatishchev 谢谢这很好用!只是一个问题,是否可以将其转换为扩展方法,例如我的问题中的两个示例之一?
  • @Jens:嗯,不是这样。扩展方法需要一个实例来运行它,但在这里你没有。
  • @abatishchev 我明白了。不过感谢您的解决方案 - 效果很好!
【解决方案2】:

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

【讨论】:

  • 我在下面添加了一个答案,其中包含如何修改 PropertyName 方法的示例,以便可以安全地与@abatishchev 答案中的原始语法一起使用。
【解决方案3】:

在 C# 6 和更高版本中,using static 的语法优势可以使这种方法的用法更具可读性。它还将为您提供早期绑定/编译时检查,因此 CodeLens 将向您展示过去作为字符串文字引用的属性的用法(例如在大量数据绑定代码中漂浮在那里)。当您必须在路上重命名属性时,这使得重构非常更容易。

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

PropertyName 方法中还有一行用于处理 @pedro-faustino 关于异常转换 UnaryExpression 的观点。 (如果该方法在 propertyExpression 参数中有第二个类型参数,用于返回类型,例如 Expression&lt;Func&lt;T, TMember&gt;&gt;,则不需要这样做。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-28
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 2019-05-16
    • 2012-01-09
    • 1970-01-01
    • 2021-11-30
    相关资源
    最近更新 更多