C# ';注入';从一个表达式转换成另一个表达式

C# ';注入';从一个表达式转换成另一个表达式,c#,linq,expression,C#,Linq,Expression,我有一个表达式的定义如下: Expression<Func<T1, T2, bool>> firstExpression; 如何使用System.Linq.Expressions?您可以通过将参数表达式与常量表达式交换来实现这一点。应该是这样的 实现自定义交换表达式Visitor public class SwapVisitor : ExpressionVisitor { public Expression From { get; set; } publ

我有一个表达式的定义如下:

Expression<Func<T1, T2, bool>> firstExpression;

如何使用
System.Linq.Expressions

您可以通过将参数表达式与常量表达式交换来实现这一点。应该是这样的

  • 实现自定义交换表达式Visitor

    public class SwapVisitor : ExpressionVisitor
    {
        public Expression From { get; set; }
        public Expression To { get; set; }
    
        public override Expression Visit(Expression node)
        {
            return node == From ? To : base.Visit(node);
        }
    }
    
  • 将参数T2替换为常数

    var swapper = new SwapVisitor
    {
      From = fistExpression.Parameters[1],
      To = Expression.Constant(val)
    };
    
    var result = Expression.Lambda<Func<T, bool>>(
       swapper.Visit(fistExpression.Body), 
       fistExpression.Parameters[0]);
    
    var swapper=新SwapVisitor
    {
    From=fistExpression.参数[1],
    To=表达式常数(val)
    };
    var result=Expression.Lambda(
    交换者访问(fistExpression.Body),
    fistExpression.参数[0]);
    

  • 考虑以下示例(firstLambda和secondExpression仅用于查看编译器如何构建表达式)

    使用系统;
    使用System.Linq.Expressions;
    静态void Main(字符串[]参数)
    {
    表达式firstExpression=(a,b)=>a==b.ToString();
    Func firstLambda=(a,b)=>a==b.ToString();
    Expression secondExpression=s=>firstLambda(s,45);//这个表达式我只需要看看它是如何编译的
    var inputParameter=Expression.Parameter(typeof(string),“s”);
    var invocation=Expression.Invoke(firstExpression,inputParameter,Expression.Constant(47));
    var ourBuildExpression=Expression.Lambda(调用,新参数Expression[]{inputParameter}).Compile();
    Console.WriteLine(ourBuildExpression(“45”));
    Console.WriteLine(ourBuildExpression(“47”));
    Console.ReadKey();
    }
    
    我在调试器监视窗口中检查了
    Expression secondExpression=s=>firstLambda(s,45)的结果它是调用表达式,所以我手动构造了相同的表达式


    您可以看到测试调用返回并按预期打印False和True。

    您可以使用expression visitor实现这一点:

    public static class EmitUtils
    {
            private class ParameterReplacerVisitor : ExpressionVisitor
            {
                private ParameterExpression _source;
                private Expression _target;
    
                public ParameterReplacerVisitor(ParameterExpression source, Expression target)
                {
                    _source = source;
                    _target = target;
                }
    
                public override Expression Visit(Expression node) =>
                    node == _source
                        ? _target
                        : base.Visit(node);
            }
    
            public static Expression ReplaceParameter(Expression body, ParameterExpression srcParameter, Expression dstParameter) =>
                new ParameterReplacerVisitor(srcParameter, dstParameter).Visit(body);
    
            public static Expression<Func<T1, T3>> BuildClosure<T1, T2, T3>(Expression<Func<T1, T2, T3>> src, T2 closureValue)
            {
                var constExpression = Expression.Constant(closureValue, typeof(T2));
                var body = ReplaceParameter(src.Body, src.Parameters[1], constExpression);
                return Expression.Lambda<Func<T1, T3>>(body, src.Parameters[0]);
            }
    
        }
    
    公共静态类EmitUtils
    {
    私有类参数replacervisitor:ExpressionVisitor
    {
    私有参数表达式_源;
    私人表达目标;
    公共参数置换器(ParameterExpression源、表达式目标)
    {
    _来源=来源;
    _目标=目标;
    }
    公共覆盖表达式访问(表达式节点)=>
    节点==\u源
    ?_目标
    :base.Visit(节点);
    }
    公共静态表达式ReplaceParameter(表达式体、参数Expression srcParameter、表达式dstParameter)=>
    新参数replacervisitor(srcParameter,dstParameter)。访问(body);
    公共静态表达式BuildClosure(表达式src,T2 closureValue)
    {
    var constExpression=表达式常数(closureValue,typeof(T2));
    var body=ReplaceParameter(src.body,src.Parameters[1],constExpression);
    返回表达式.Lambda(body,src.Parameters[0]);
    }
    }
    
    以下是示例用法:

            [Test]
            public void ClosureTest()
            {
                Expression<Func<int, string, bool>> CheckStringLength = (len, str) => str.Length < len;
                var constString = "some string";
                var result = EmitUtils.BuildClosure(CheckStringLength, constString);
                Assert.That(result.Compile().Invoke(100), Is.True);
            }
    
    [测试]
    公共无效ClosureTest()
    {
    表达式CheckStringLength=(len,str)=>str.Length
    是的,这是可行的,但有一个问题:Expression.Invoke不适用于大多数查询提供程序。。。
    using System;
    using System.Linq.Expressions;
    
    static void Main(string[] args)
    {
        Expression<Func<string, int, bool>> firstExpression = (a, b) => a == b.ToString();
    
        Func<string, int, bool> firstLambda = (a, b) => a == b.ToString();
        Expression<Func<string, bool>> secondExpression = s => firstLambda(s, 45); // this expression I need just to see how it is compiled
    
    
        var inputParameter = Expression.Parameter(typeof(string), "s");
    
        var invocation = Expression.Invoke(firstExpression, inputParameter, Expression.Constant(47));
    
        var ourBuildExpression = Expression.Lambda<Func<string, bool> > (invocation, new ParameterExpression[] { inputParameter }).Compile();
    
        Console.WriteLine(ourBuildExpression("45"));
        Console.WriteLine(ourBuildExpression("47"));
    
        Console.ReadKey();
    }
    
    public static class EmitUtils
    {
            private class ParameterReplacerVisitor : ExpressionVisitor
            {
                private ParameterExpression _source;
                private Expression _target;
    
                public ParameterReplacerVisitor(ParameterExpression source, Expression target)
                {
                    _source = source;
                    _target = target;
                }
    
                public override Expression Visit(Expression node) =>
                    node == _source
                        ? _target
                        : base.Visit(node);
            }
    
            public static Expression ReplaceParameter(Expression body, ParameterExpression srcParameter, Expression dstParameter) =>
                new ParameterReplacerVisitor(srcParameter, dstParameter).Visit(body);
    
            public static Expression<Func<T1, T3>> BuildClosure<T1, T2, T3>(Expression<Func<T1, T2, T3>> src, T2 closureValue)
            {
                var constExpression = Expression.Constant(closureValue, typeof(T2));
                var body = ReplaceParameter(src.Body, src.Parameters[1], constExpression);
                return Expression.Lambda<Func<T1, T3>>(body, src.Parameters[0]);
            }
    
        }
    
            [Test]
            public void ClosureTest()
            {
                Expression<Func<int, string, bool>> CheckStringLength = (len, str) => str.Length < len;
                var constString = "some string";
                var result = EmitUtils.BuildClosure(CheckStringLength, constString);
                Assert.That(result.Compile().Invoke(100), Is.True);
            }