【问题标题】:Access Property Using Reflection C#使用反射 C# 访问属性
【发布时间】:2017-05-11 11:02:38
【问题描述】:

我想出了一个函数,它按指定的值对列表进行排序,然后返回一个新的对象值。当我试图获取StatValue 时,我被困在 .Select 语句中。下面的当前示例是我正在尝试做的(显然不起作用)

我可以得到一个按属性顺序传递的字符串值,我在获取StatValue时需要使用这个属性字符串,即x.GoalsPerGame

private static TodayStatsItem GetStatsValue<TOrderBy>(Expression<Func<PlayerItem, bool>> filter,
    Expression<Func<PlayerItem, TOrderBy>> orderBy, IEnumerable<PlayerItem> items, string desc)
{
    var itemSorted = new TodayStatsItem {
        PlayerItems = items.AsQueryable().OrderBy(orderBy).Select(x => new TodayStatsViewModel
        {
            Name = x.Name,
            //StatValue = ((MemberExpression)orderBy.Body).Member.Name.ToString() // returns GoalsPerGame
            StatValue = x.((MemberExpression)orderBy.Body).Member.Name.ToString() // attempt to access property
        }),
        StatName = desc        
    };
    return itemSorted;
}

对该方法的调用如下所示。

this.LeagueItem.Add(GetStatsValue(null, x => x.GoalsPerGame, this.GameReport, "Goals Per Game"));

有什么方法可以做到这一点吗?谢谢。

【问题讨论】:

  • 您应该能够(重新)使用给定的orderBy 表达式来获得所需的值(Compile().Invoke(item) - 尽管您可能只想编译一次并重用生成的函数)。据我所知,不需要反思。
  • 你能举个例子吗?这也会比反射更有效吗?
  • orderBy.Compile() 给你一个Func&lt;PlayerItem, TOrderBy&gt;。在您的示例中,这将是一个返回给定播放器项目的 GoalsPerGame 值的函数。因此,您只需在每个项目上调用该函数:StatValue = compiledOrderBy.Invoke(x) - 或只是 StatValue = compiledOrderBy(x)。至于性能,反射往往涉及额外的检查,因此通常比较慢。
  • 刚刚实现了这个,它也可以完美运行。现在我只需要决定哪个最适合该应用程序。感谢您的建议。

标签: c# linq generics expression


【解决方案1】:

下面是一种使用反射从具有指定属性名称的对象中获取属性值的方法:

using System.Reflection;

public static object GetPropertyValue(object obj, string propertyName)
{
    PropertyInfo property = obj.GetType().GetProperty(propertyName);
    return property.GetValue(obj);
}

在你的情况下:

string propertyName = ((MemberExpression)orderBy.Body).Member.Name.ToString();
StatValue = (int)GetPropertyValue(x, propertyName); // I'm assuming StatValue is of type int

但是要实现你想要做的,你可以先compileorderBy表达式然后用它来获取值,这比反射更快。

像这样:

private static TodayStatsItem GetStatsValue<TOrderBy>(Expression<Func<PlayerItem, bool>> filter,
Expression<Func<PlayerItem, TOrderBy>> orderBy, IEnumerable<PlayerItem> items, string desc)
{
    Func<PlayerItem, TOrderBy> orderByDeleg = orderBy.Compile();
    var itemSorted = new TodayStatsItem {
    PlayerItems = items.AsQueryable().OrderBy(orderByDeleg).Select(x => new TodayStatsViewModel
        {
            Name = x.Name,
            StatValue = orderByDeleg(x)
        }),
        StatName = desc
    };
    return itemSorted;
}

【讨论】:

  • 这非常有效。我看到 Pieter 在 OP 上发表的评论表明可能不需要反射。您是否碰巧知道这两种方法中哪一种更有效?也感谢您的回答,它真的很有帮助!
  • 检查我更新的答案。第二种方式,使用编译委托,比使用反射更快。反射(一般来说)相当慢。
  • 使用orderByDeleg更好,因为它不需要再次编译。我更新了我的答案。
  • @BadDub:那是因为 Func 已经编译好了。表达式实际上是代表原始代码的树状数据结构,除非您编译它,否则它是不可执行的。对 Linq-to-SQL 很有用(将表达式转换为 SQL,因此数据库可以进行过滤和排序),但在处理内存中的对象时会更慢且更麻烦。
  • 您传递给方法的x =&gt; x.GoalsPerGame 被编译为函数或Expression 树结构,具体取决于参数类型。在后一种情况下,它是Expression.Lambda(typeof(Func&lt;PlayerItem, TOrderBy&gt;), Expression.MakeMemberAccess(parameter, typeof(PlayerItem).GetProperty("GoalsPerGame")), parameter) 的简写,其中parameterExpression.Parameter(typeof(TOrderBy), "x") 的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-03
  • 2023-03-31
  • 2010-11-21
  • 2012-08-08
  • 1970-01-01
  • 2017-05-31
  • 2010-11-14
相关资源
最近更新 更多