转换表达式树类型 [英] Convert expression tree types
问题描述
我一直在寻找最低的SO,以找到解决我问题的方法.
I've searched high an low of SO to find a solution for my problem.
我已经找到了一些简单表达式之类的答案
I've found several answers for when it comes to simple expressions like
var exp1 Expression<Func<T, bool>> x => x.Name == "MyName"
但是当表达式类似时,我遇到了麻烦
But I'm having trouble when the expressions are like:
var exp1 Expression<Func<T, bool>> x => x.Category.Name == "Coupe"
对于简单的表达式,我可以将任何表达式从一种类型(T)转换为另一种类型(TT),在其他情况下(更复杂的情况下,我也需要这样做)
For the simple ones, I am able to convert any expression from one type (T) to another (TT), I need to do it also in the other cases, more complex...
有人可以提供一些帮助吗?这是到目前为止我得到的:
Anyone who can help with some pointers? Here is what I've got so far:
private class CustomVisitor<T> : ExpressionVisitor
{
private readonly ParameterExpression mParameter;
public CustomVisitor(ParameterExpression parameter)
{
mParameter = parameter;
}
//this method replaces original parameter with given in constructor
protected override Expression VisitParameter(ParameterExpression node)
{
return mParameter;
}
private int counter = 0;
/// <summary>
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression" />.
/// </summary>
/// <param name="node">The expression to visit.</param>
/// <returns>
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
/// </returns>
/// <exception cref="System.NotImplementedException"></exception>
protected override Expression VisitMember(MemberExpression node)
{
counter++;
System.Diagnostics.Debug.WriteLine("{0} - {1}", node.ToString(), counter);
try
{
//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);
}
catch (Exception ex)
{
return null;
}
}
}
实用方法:
public static Expression<Func<TDestin, T>> ConvertTypesInExpression<TSource, TDestin, T>(Expression<Func<TSource, T>> source)
{
var param = Expression.Parameter(typeof(TDestin));
var body = new CustomVisitor<TDestin>(param).Visit(source.Body);
Expression<Func<TDestin, T>> lambda = Expression.Lambda<Func<TDestin, T>>(body, param);
return lambda;
}
它的用法如下:
var changedFilter = ConvertTypesInExpression<ClientNotificationRuleDto, ClientNotificationRule, bool>(filterExpression);
因此,如果任何人都可以提供一些想法或建议,那将很棒!
So if anyone can help with some ideias or pointers, that would be great!
推荐答案
分析此测试:
class Replaced
{
public Inner Inner { get; set; }
}
class Inner
{
public string Name { get; set; }
}
class Replacing
{
public Inner Inner { get; set; }
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var parameter = Expression.Parameter(typeof(Replacing));
var visitor = new CustomVisitor(parameter);
Expression<Func<Replaced, bool>> expression = x => x.Inner.Name == "ss";
var resultExpression = (Expression<Func<Replacing, bool>>)visitor.Visit(expression);
var function = resultExpression.Compile();
var result = function(new Replacing
{
Inner = new Inner
{
Name = "ss"
}
});
Assert.IsTrue(result);
}
}
internal class CustomVisitor : ExpressionVisitor
{
private readonly ParameterExpression mParameter;
private int counter = 0;
public CustomVisitor(ParameterExpression parameter)
{
mParameter = parameter;
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(
Visit(node.Body),
node.Parameters.Select(x => (ParameterExpression)Visit(x)).ToArray());
//or simpler but less generic
//return Expression.Lambda(Visit(node.Body), mParameter);
}
//this method will be called twice first for Name and then for Inner
protected override Expression VisitMember(MemberExpression node)
{
counter++;
System.Diagnostics.Debug.WriteLine("{0} - {1}", node.ToString(), counter);
if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
throw new NotImplementedException();
var memberName = node.Member.Name;
var inner = Visit(node.Expression);
var otherMember = inner.Type.GetProperty(memberName);
return Expression.Property(inner, otherMember);
}
protected override Expression VisitParameter(ParameterExpression node)
{
return mParameter;
}
}
请注意,访问成员被两次调用,并且必须对两个调用做出相应的反应.另外,您还需要覆盖lambda创建,因为它会导致参数替换失败.
Note that visit member is called twice and must react accordingly for both calls. Also you need to override the lambda creation as it would fail in parameter replacement.
PS:永远不要抓住基类Exception,这只是一种不好的做法,而对异常的恐慌返回null就是错误的.
PS: Never catch base class Exception its just bad practice and the panic return null on exception is just wrong.
这篇关于转换表达式树类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!