Преобразовать сигнатуру метода и изменить тело метода - PullRequest
0 голосов
/ 22 января 2020

Я пытаюсь изменить выражение функции. Я хочу заменить входной аргумент функции на функцию, которая возвращает то же значение. Идея состоит в том, чтобы преобразовать: 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» и измените его, чтобы не посещать детей этого типа. '

1 Ответ

0 голосов
/ 22 января 2020

Через пару часов я нашел что-то, что работает.

PS: Не уверен, является ли это наиболее универсальным c или лучшим решением для этой проблемы.

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