使用Linq表达式和反射来获取属性值的通用方法 [英] Generic method to get property values with Linq Expression and reflection
问题描述
亲爱的反思之神
我想要一个通用的 GetValue< TEntity,T>
方法可以返回以下属性值,给出以下 User
class:
public class User
{
public int Id {get;组; }
public int ClientId {get;组; }
public string UserName {get;组; }
public string FirstName {get;组; }
public string LastName {get;组; }
public string MobileNumber {get;组; }
public bool IsActive {get;组; }
公共客户端客户端{get;组; }
public List< Package> Packages {get;组; }
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);
到目前为止,我有以下方法可用于前三种情况:
public T GetValue< TEntity,T>(表达式< Func< TEntity,T>> propertyExpression)其中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]);
抛出:无法投射类型为System.Linq.Expressions的对象。 InstanceMethodCallExpressionN'来输入'System.Linq.Expressions.MemberExpression'。
var packageName = dataCollector。 GetValue< User,string>(x => x.Packages [0] .Name);
抛出:对象与目标类型不匹配
var clientName = dataCollector.GetValue< User,string>(x => x.Client.Name);
抛出:对象与目标类型不匹配
我需要对该方法进行哪些更改?
现在,我将牺牲一个USB闪存驱动器,同时等待您的答案:)
问题出在这里:
if(response!= null)
{
var expr =(MemberExpression)propertyExpression.Body;
var prop =(PropertyInfo)expr.Member;
return(T)prop.GetValue(response);
$ b 只有当您的表达式引用属性时, 直接,否则 propertyExpression.Body
不会是 MemberExpression
,你会得到一个运行时抛出错误。不起作用的三个不直接引用一个属性 - 前两个属性顶部的方法(索引器),最后一个引用嵌套属性。
因为所有你想要的都是表达式的值,我认为你可以这样做:
if(response!= null)
{
Func< TEntity,T> func = propertyExpression.Compile();
返回func(响应);
$ / code>
如果您打算用其他表达式(如获取属性的 name ),那么您需要决定是否要支持不直接引用属性的表达式并为其添加处理程序。
Dear Gods of Reflection
I would like to have a generic GetValue<TEntity, T>
method that can return the following property values given the following User
class:
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; }
}
Example usage of what GetValue<TEntity, T>
should be able to do:
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);
So far I have the following method which works for the first 3 scenarios:
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);
}
But it does not work for the last 3 scenarios:
var packageFirst = dataCollector.GetValue<User, Package>(x => x.Packages[0]);
Throws: 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);
Throws: Object does not match target type.
var clientName = dataCollector.GetValue<User, string>(x => x.Client.Name);
Throws: Object does not match target type.
What changes do I need to make to the method?
I shall now sacrifice a USB flash drive whilst awaiting your answers :)
解决方案 The problem is here:
if (response != null)
{
var expr = (MemberExpression)propertyExpression.Body;
var prop = (PropertyInfo)expr.Member;
return (T)prop.GetValue(response);
}
This only works if your expression references a property directly, otherwise propertyExpression.Body
will not be a MemberExpression
and you'll get a run-time cast error. The three that don't work do not reference a property directly - the first two reverence a method on top of a property (the indexer) and the last references a nested property.
Since all you want is the value of the expression, though, I think you can just do:
if (response != null)
{
Func<TEntity, T> func = propertyExpression.Compile();
return func(response);
}
If you intended to do other things with the expression (like get the name of the property), then you'll need to decide if you want to support expressions that don't reference a property directly and add handlers for that.
这篇关于使用Linq表达式和反射来获取属性值的通用方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!