【问题标题】:Generic method to get property values with Linq Expression and reflection使用 Linq 表达式和反射获取属性值的通用方法
【发布时间】:2016-10-26 23:08:55
【问题描述】:

亲爱的反射之神

我想要一个通用的GetValue<TEntity, T> 方法,它可以在给定以下User 类的情况下返回以下属性值:

public class User  
{
   public int Id { get; set; }
   public int ClientId { get; set; }
   public string UserName { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string MobileNumber { get; set; }   
   public bool IsActive { get; set; }

   public Client Client { get; set; }
   public List<Package> Packages { get; set; }

 }

GetValue&lt;TEntity, T&gt; 应该能够做什么的示例用法:

  var firstName = dataCollector.GetValue<User, string>(x => x.FirstName);
  var client = dataCollector.GetValue<User, Client>(x => x.Client);
  var packages = dataCollector.GetValue<User, List<Package>>(x => x.Packages);

  var packageFirst = dataCollector.GetValue<User, Package>(x => x.Packages[0]);
  var packageName = dataCollector.GetValue<User, string>(x => x.Packages[0].Name);
  var clientName = dataCollector.GetValue<User, string>(x => x.Client.Name);

到目前为止,我有以下方法适用于前 3 个场景:

 public T GetValue<TEntity, T>(Expression<Func<TEntity, T>> propertyExpression) where TEntity : class
 {
    var response = _responses.FirstOrDefault(p => p.GetType() == typeof(TEntity)) as TEntity;
    if (response != null)
    {
       var expr = (MemberExpression)propertyExpression.Body;
       var prop = (PropertyInfo)expr.Member;
       return (T)prop.GetValue(response);
    }
    return default(T);
  }

但它不适用于最后 3 个场景:

  var packageFirst = dataCollector.GetValue<User, Package>(x => x.Packages[0]);

抛出:Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'.

  var packageName = dataCollector.GetValue<User, string>(x => x.Packages[0].Name);

抛出:Object does not match target type.

  var clientName = dataCollector.GetValue<User, string>(x => x.Client.Name);

抛出:Object does not match target type.

我需要对方法进行哪些更改?

我现在将在等待您的回答的同时牺牲一个 USB 闪存驱动器:)

【问题讨论】:

  • “不起作用”是什么意思?不编译?错误信息?如果 x.Packages 为空或 null,或者如果 x.Client 为 null,您将收到运行时错误。
  • 我们需要更多的答案:谁是 dataCollector?哪种类型?你想在哪里得到这个?我认为你把事情复杂化了。
  • @DStanley 它运行但最后 3 次抛出错误。查看我编辑的答案

标签: c# generics reflection linq-expressions


【解决方案1】:

问题出在这里:

if (response != null)
{
   var expr = (MemberExpression)propertyExpression.Body;
   var prop = (PropertyInfo)expr.Member;
   return (T)prop.GetValue(response);
}

在您的表达式直接引用属性时有效,否则propertyExpression.Body 将不是MemberExpression,您将收到运行时转换错误.不工作的三个不直接引用属性 - 前两个尊重属性顶部的方法(索引器),最后一个引用嵌套属性。

由于你想要的只是表达式的值,我认为你可以这样做:

if (response != null)
{
   Func<TEntity, T> func = propertyExpression.Compile();  
   return func(response);
}

如果您打算用表达式做 其他 事情(例如获取属性的 名称),那么您需要决定是否要支持表达式不直接引用属性并为此添加处理程序。

【讨论】:

  • 是的,这似乎是问题所在。会尝试你的建议。我只需要属性值
  • 太棒了!这样可行!我猜 Compile() 在后台做了很多魔法。以为我需要一些递归来获取嵌套属性
  • 您可以删除 Linq.Expression 和 Compile() 并执行以下操作:` public T GetValue(Func function) where TEntity : class { var response = _responses .FirstOrDefault(p => p.GetType() == typeof(TEntity)) 作为 TEntity;如果(响应!= null)返回函数(响应);返回默认值(T); }`
  • 没错,但使用Expression 的一个好处是,如果您曾经确实想要查看表达式的元数据,您可以在不更改方法签名的情况下这样做,这将是一个重大变化。
  • 当然,因为您不需要在运行时编译表达式。这是否是一个 重大 问题取决于调用它时还会发生什么。如果是这样,那么妥协可能是添加已编译表达式的缓存。
【解决方案2】:

您只需执行 Lambda 表达式即可:

        if (response != null)
        {
            return propertyExpression.Compile().Invoke(response);
        }

【讨论】:

  • 返回(T)propertyExpression.Compile().DynamicInvoke(response);func(response)有区别吗?
  • 我已经编辑了我的答案,现在恕我直言,没有什么不同:由于类型是已知的,使用 DynamicInvoke 来使用反射是没有意义的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
相关资源
最近更新 更多