首页 > 解决方案 > 转换方法签名和修改方法体

问题描述

我正在尝试修改函数的表达式。我想用返回相同值的函数替换函数输入参数。function (Foo foo) { return foo.ToString(); } 这个想法是转换:function(Func<Foo> fooProvider) { return fooProvider().ToString() }

其实我什至不想调用传递给我们的函数,而是直接使用函数体。所以真正的例子没有得到一个Func<Foo>作为输入,而是一个Expression<Func<Foo>>.

示例代码可以在下面找到。我假设我现在遇到了一个异常,因为我更改了函数的签名。虽然这正是我想做的。

一些代码:程序

class Program
{
   public static void Run()
   {
      Expression<Func<int, bool>> isOdd = number => (number % 2) == 1;
      Expression<Func<string, int>> parseTextToNumber = text => Convert.ToInt32(text);

      var vistor = new ReplaceExpressionVisitor(isOdd.Parameters[0], parseTextToNumber.Body);
      var result = vistor.Visit(isOdd);

      Expression<Func<string, bool>> textRepresentsOddNumber = result as dynamic;

      //Expected something like this:
      Expression<Func<string, bool>> expectedResult = text => (Convert.ToInt32(text) % 2) == 1;
   }
}

访客类

private class ReplaceExpressionVisitor : ExpressionVisitor
{
    private readonly Expression _oldValue;
    private readonly Expression _newValue;

    public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
    {
        _oldValue = oldValue;
        _newValue = newValue;
    }

    public override Expression Visit(Expression node)
    {
        if (node == _oldValue)
            return _newValue;
        return base.Visit(node);
    }
}

和错误信息:

System.InvalidOperationException:'当从'VisitLambda'调用时,重写'System.Linq.Expressions.ParameterExpression'类型的节点必须返回相同类型的非空值。或者,覆盖“VisitLambda”并将其更改为不访问这种类型的孩子。

标签: c#expressionroslyn

解决方案


几个小时后,我确实发现了一些有用的东西。

PS:不确定这是否是解决此问题的最通用或最佳解决方案。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

public class Program
{
    public static void Run()
    {
        Expression<Func<int, bool>> isOdd = number => (number % 2) == 0;
        Expression<Func<string, int>> parseTextToNumber = text => Convert.ToInt32(text);

        var visitor = new ReplaceExpressionVisitor(isOdd.Parameters[0], parseTextToNumber.Body);
        var result = Expression.Lambda(visitor.Visit(isOdd.Body), parseTextToNumber.Parameters);

        Expression<Func<string, bool>> textRepresentsOddNumber = result as dynamic;

        //test:
        var code = textRepresentsOddNumber.Compile();
        Console.WriteLine(code.DynamicInvoke("0"));//writes false
        Console.WriteLine(code.DynamicInvoke("1"));//writes true
    }
}

private class ReplaceExpressionVisitor : ExpressionVisitor
{
    private readonly Expression _oldValue;
    private readonly Expression _newValue;

    public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
    {
        _oldValue = oldValue;
        _newValue = newValue;
    }

    public override Expression Visit(Expression node)
    {
        if (node == _oldValue)
            return _newValue;
        return base.Visit(node);
    }
}

推荐阅读