转换表达式树类型[英] Convert expression tree types

本文是小编为大家收集整理的关于转换表达式树类型的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我已经搜索了高低,因此为我的问题找到解决方案.

我找到了一些简单表达式的答案,例如

var exp1 Expression<Func<T, bool>> x => x.Name == "MyName"

,但是当表达式像:

时,我遇到了麻烦
var exp1 Expression<Func<T, bool>> x => x.Category.Name == "Coupe"

对于简单的

,我能够将任何一种表达式从一种类型(t)转换为另一种(tt),在其他情况下,我也需要这样做,更复杂...

有人可以帮助一些指针吗?这是我到目前为止所拥有的:

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

因此,如果有人可以帮助一些伊迪亚斯或指针,那将是很棒的!

推荐答案

分析此测试:

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的创建,因为它将在参数更换中失败.

ps:永远不要赶上基类例外,它只是不良习惯,而异常的恐慌回报是错误的.

其他推荐答案

在@rafal的宝贵帮助下,并从 https://stackoverflow commights提供了见解.需要

public static class EXpressionTreeTools
{
    #region ConvertTypesInExpression

    /// <summary>
    /// Converts the types in the expression.
    /// </summary>
    /// <typeparam name="TSource">The source type (the "replacee").</typeparam>
    /// <typeparam name="TDestin">The destiny type (the replacer).</typeparam>
    /// <typeparam name="T">The type of the result fo the expression evaluation</typeparam>
    /// <param name="source">The source expression.</param>
    /// <returns></returns>
    public static Expression<Func<TDestin, T>> ConvertTypesInExpression<TSource, TDestin, T>(Expression<Func<TSource, T>> source)
    {
        var parameter = Expression.Parameter(typeof(TDestin));
        var visitor = new CustomVisitor(parameter);
        //Expression<Func<TSource, bool>> expression = x => x.Inner.Name == "ss";
        Expression<Func<TDestin, T>> resultExpression = (Expression<Func<TDestin, T>>)visitor.Visit(source);

        return resultExpression;
    }

    #endregion

    #region CustomVisitor

    /// <summary>
    /// A custom "visitor" class to traverse expression trees
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class CustomVisitor : ExpressionVisitor
    {
        private readonly ParameterExpression mParameter;

        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)
        {
            if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
            //throw new NotImplementedException();
            {
                Expression exp = this.Visit(node.Expression);

                if (exp == null || exp is ConstantExpression) // null=static member
                {
                    object @object = exp == null ? null : ((ConstantExpression)exp).Value;
                    object value = null; Type type = null;
                    if (node.Member is FieldInfo)
                    {
                        FieldInfo fi = (FieldInfo)node.Member;
                        value = fi.GetValue(@object);
                        type = fi.FieldType;
                    }
                    else if (node.Member is PropertyInfo)
                    {
                        PropertyInfo pi = (PropertyInfo)node.Member;
                        if (pi.GetIndexParameters().Length != 0)
                            throw new ArgumentException("cannot eliminate closure references to indexed properties");
                        value = pi.GetValue(@object, null);
                        type = pi.PropertyType;
                    }
                    return Expression.Constant(value, type);
                }
                else // otherwise just pass it through
                {
                    return Expression.MakeMemberAccess(exp, node.Member);
                }
            }
            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;
        }
    }

    #endregion
}

本文地址:https://www.itbaoku.cn/post/1557006.html

问题描述

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"

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

Utility method:

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

And it's being used like this:

var changedFilter = ConvertTypesInExpression<ClientNotificationRuleDto, ClientNotificationRule, bool>(filterExpression);

So if anyone can help with some ideias or pointers, that would be great!

推荐答案

Analyze this test:

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

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: Never catch base class Exception its just bad practice and the panic return null on exception is just wrong.

其他推荐答案

With the precious help from @Rafal and insights from this, I managed to achieve a solution for my needs

public static class EXpressionTreeTools
{
    #region ConvertTypesInExpression

    /// <summary>
    /// Converts the types in the expression.
    /// </summary>
    /// <typeparam name="TSource">The source type (the "replacee").</typeparam>
    /// <typeparam name="TDestin">The destiny type (the replacer).</typeparam>
    /// <typeparam name="T">The type of the result fo the expression evaluation</typeparam>
    /// <param name="source">The source expression.</param>
    /// <returns></returns>
    public static Expression<Func<TDestin, T>> ConvertTypesInExpression<TSource, TDestin, T>(Expression<Func<TSource, T>> source)
    {
        var parameter = Expression.Parameter(typeof(TDestin));
        var visitor = new CustomVisitor(parameter);
        //Expression<Func<TSource, bool>> expression = x => x.Inner.Name == "ss";
        Expression<Func<TDestin, T>> resultExpression = (Expression<Func<TDestin, T>>)visitor.Visit(source);

        return resultExpression;
    }

    #endregion

    #region CustomVisitor

    /// <summary>
    /// A custom "visitor" class to traverse expression trees
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class CustomVisitor : ExpressionVisitor
    {
        private readonly ParameterExpression mParameter;

        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)
        {
            if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
            //throw new NotImplementedException();
            {
                Expression exp = this.Visit(node.Expression);

                if (exp == null || exp is ConstantExpression) // null=static member
                {
                    object @object = exp == null ? null : ((ConstantExpression)exp).Value;
                    object value = null; Type type = null;
                    if (node.Member is FieldInfo)
                    {
                        FieldInfo fi = (FieldInfo)node.Member;
                        value = fi.GetValue(@object);
                        type = fi.FieldType;
                    }
                    else if (node.Member is PropertyInfo)
                    {
                        PropertyInfo pi = (PropertyInfo)node.Member;
                        if (pi.GetIndexParameters().Length != 0)
                            throw new ArgumentException("cannot eliminate closure references to indexed properties");
                        value = pi.GetValue(@object, null);
                        type = pi.PropertyType;
                    }
                    return Expression.Constant(value, type);
                }
                else // otherwise just pass it through
                {
                    return Expression.MakeMemberAccess(exp, node.Member);
                }
            }
            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;
        }
    }

    #endregion
}