如何更改表达式目录树类型? [英] How to change a type in an expression tree?
问题描述
我有这样一个方法:
私人布尔Method_1(表达式来; Func键< IPerson,布尔>>表达式)
{
/ *一些代码,将调用Method_2 * /
}
在这个方法我想改变 IPerson
键入另一种类型。我想打电话,看起来像这样的另一种方法:
私人布尔Method_2(表达式来; Func键< PersonData,布尔>>表达)
{
/ *一些代码* /
}
所以,在 method_1
我需要改变 IPerson
到 PersonData code> 。我怎样才能做到这一点。
编辑:
当我打电话: Method_1( p => p.Id == 1)
我要拯救的条件( p.Id == 1
),但我想在另一种类型执行这个条件,即 IPerson
。所以,我需要改变的表达或创建 IPerson
编辑II新的表达式:
对于那些谁感兴趣的话,这就是(现在)我的解决方案:
私有类CustomExpressionVisitor< T> :ExpressionVisitor
{
ParameterExpression _parameter;
公共CustomExpressionVisitor(ParameterExpression参数)
{
_parameter =参数;
}
保护覆盖表达VisitParameter(ParameterExpression节点)
{
返回_parameter;
}
保护覆盖表达VisitMember(MemberExpression节点)
{
如果(node.Member.MemberType == System.Reflection.MemberTypes.Property)
{
MemberExpression memberExpression = NULL;
VAR memberName = node.Member.Name;
VAR otherMember = typeof运算(T).GetProperty(memberName);
memberExpression = Expression.Property(访问(node.Expression),otherMember);
返回memberExpression;
}
,否则
{
返回base.VisitMember(节点);
}
}
}
这是我的方式使用它:
公共虚拟BOOL存在(表达式来; Func键< DTO,布尔>>表达式)
{
VAR参数= Expression.Parameter(typeof运算(I));
VAR的结果=新CustomExpressionVisitor< I>(参数).Visit(expression.Body);
表达式来; Func键<我,布尔>>波长= Expression.Lambda<&Func键LT;我,布尔>>(结果,参数);
布尔存在= _repository.Exists(拉姆达);
回报率的存在;
}
如果你使用很容易.NET 4中(更新:在注释中提到的第4版4.5不加入 ExpressionVisitor
),这将需要一些谷歌上搜索老年人框架:
有一些假设,但我认为它们是有效为您的DTO和实体场景 - 访问必须匹配的属性。
类PersonData
{
公共BOOL道具{搞定;组; }
}
接口IPerson
{
布尔道具{搞定;组; }
}
在.NET 4中有 ExpressionVisitor
类定义使这是一个容易得多,如果你使用旧之一,那么你需要写或发现实施它:
类参观者< T> :ExpressionVisitor
{
ParameterExpression _parameter;
//必须有每个参数
参数表达的只有一个实例//有一个,这样一个在这里通过
公众参观(ParameterExpression参数)
{
_parameter =参数;
}
//此方法取代,在构造函数中
保护覆盖表达VisitParameter(ParameterExpression节点)
{
返回_parameter给原来的参数;
}
//因为PersonData没有实现IPerson这个是必需的,它发现
//属性PersonData具有相同的名称作为一个在表达$ B $引用b //并宣布IPerson
保护覆盖表达VisitMember(MemberExpression节点)
{
//如果你使用的字段,那么你需要扩展
属性只有允许//这方法来处理它们
如果(node.Member.MemberType = System.Reflection.MemberTypes.Property!)
抛出新NotImplementedException();
//矿山道具
VAR memberName = node.Member.Name原始表达式中引用你的
//样品标识成员的名称;
的名字
VAR otherMember = typeof运算(T).GetProperty(memberName)//找到T型(= PersonData)财产;
//访问给这个表达式p.Id的一面,这将是p
变种内=访问(node.Expression);
返回Expression.Property(内,otherMember);
}
}理念
证明:
类节目
{
静态无效的主要()
{
//样品表达
表达式来; Func键< IPerson,布尔>>表达式= X => x.Prop;
//参数将在产生表达
VAR参数用于= Expression.Parameter(typeof运算(PersonData));
//访问原始表达式的身体,让我们的新的表达
变种体=新访客与LT的身体; PersonData>(参数).Visit(expression.Body);
//生成lambda表达式形式的身体和参数
//注意,这是你需要调用Method_2
表达式来什么,Func键< PersonData,布尔>>波长= Expression.Lambda<&Func键LT; PersonData,布尔>>(机身,参数);
//编译和产生方法的执行只是为了证明它的作品
VAR boolValue = lambda.Compile()(新PersonData());
}
}
请注意,这将简单表达式的工作。如果您需要处理 x.Prop.Prop1< 3
则需要进一步扩大这一点。
I have a method like this:
private bool Method_1(Expression<Func<IPerson, bool>> expression)
{
/* Some code that will call Method_2 */
}
In this method I want to change the IPerson
type to another type. I want to call another method that looks like this:
private bool Method_2(Expression<Func<PersonData, bool>> expression)
{
/* Some code */
}
So, in method_1
I need to change IPerson
to PersonData
. How can I do this?
Edit:
When I call: Method_1(p => p.Id == 1)
I want to 'save' the condition (p.Id == 1
) but I want to execute this condition on another type, namely IPerson
. So, I need to alter the expression or create a new expression with IPerson
EDIT II:
For those who are interested, this is (for now) my solution:
private class CustomExpressionVisitor<T> : ExpressionVisitor
{
ParameterExpression _parameter;
public CustomExpressionVisitor(ParameterExpression parameter)
{
_parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.MemberType == System.Reflection.MemberTypes.Property)
{
MemberExpression memberExpression = null;
var memberName = node.Member.Name;
var otherMember = typeof(T).GetProperty(memberName);
memberExpression = Expression.Property(Visit(node.Expression), otherMember);
return memberExpression;
}
else
{
return base.VisitMember(node);
}
}
}
And this is the way I use it:
public virtual bool Exists(Expression<Func<Dto, bool>> expression)
{
var param = Expression.Parameter(typeof(I));
var result = new CustomExpressionVisitor<I>(param).Visit(expression.Body);
Expression<Func<I, bool>> lambda = Expression.Lambda<Func<I, bool>>(result, param);
bool exists = _repository.Exists(lambda);
return exists;
}
It is easy if you use .net 4 (update: as noted in comment ExpressionVisitor
was added in version 4 not 4.5) it would require some googling for older frameworks:
There are some assumptions but I think they are valid for your DTO and Entity scenario - properties accessed must match.
class PersonData
{
public bool Prop { get; set; }
}
interface IPerson
{
bool Prop { get; set; }
}
In .net 4 there is ExpressionVisitor
class defined that makes this a lot easier if you use older one then you need to write or find implementation of it:
class Visitor<T> : ExpressionVisitor
{
ParameterExpression _parameter;
//there must be only one instance of parameter expression for each parameter
//there is one so one passed here
public Visitor(ParameterExpression parameter)
{
_parameter = parameter;
}
//this method replaces original parameter with given in constructor
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameter;
}
//this one is required because PersonData does not implement IPerson and it finds
//property in PersonData with the same name as the one referenced in expression
//and declared on IPerson
protected override Expression VisitMember(MemberExpression node)
{
//only properties are allowed if you use fields then you need to extend
// this method to handle them
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
throw new NotImplementedException();
//name of a member referenced in original expression in your
//sample Id in mine Prop
var memberName = node.Member.Name;
//find property on type T (=PersonData) by name
var otherMember = typeof(T).GetProperty(memberName);
//visit left side of this expression p.Id this would be p
var inner = Visit(node.Expression);
return Expression.Property(inner, otherMember);
}
}
Proof of concept:
class Program
{
static void Main()
{
//sample expression
Expression<Func<IPerson, bool>> expression = x => x.Prop;
//parameter that will be used in generated expression
var param = Expression.Parameter(typeof(PersonData));
//visiting body of original expression that gives us body of the new expression
var body = new Visitor<PersonData>(param).Visit(expression.Body);
//generating lambda expression form body and parameter
//notice that this is what you need to invoke the Method_2
Expression<Func<PersonData, bool>> lambda = Expression.Lambda<Func<PersonData, bool>>(body, param);
//compilation and execution of generated method just to prove that it works
var boolValue = lambda.Compile()(new PersonData());
}
}
Note that this will work for simple expressions. If you need to handle x.Prop.Prop1 < 3
then you need to extend this further.
这篇关于如何更改表达式目录树类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!