使用Linq表达式和反射获取属性值的通用方法

时间:2016-10-26 13:23:07

标签: c# generics reflection linq-expressions

亲爱的反思之神

我希望有一个通用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<TEntity, T>应该能够执行的示例用法:

  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闪存盘:)

2 个答案:

答案 0 :(得分:3)

问题在于:

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

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

答案 1 :(得分:1)

您只需执行Lambda表达式即可完成此操作:

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