Вернуть использованные значения из делегата? - PullRequest
1 голос
/ 10 февраля 2020

Мне было интересно, можно ли получить значения параметров и переменных, используемых внутри тела функции лямбда-выражения в C#.

Например:

decimal a = -0.5m;
decimal b = 2m;
Func<decimal,decimal> f = t => a*t + b; // Linear equation of t
Console.WriteLine(FunctionToString(f)); // Generate something like "-0.5 * t + 2"

Я попытался переопределить лямбда-функцию внутри выражения, например:

decimal a = -0.5m;
decimal b = 2m;
Expression<Func<decimal,decimal>> xf = t => a*t + b; // Linear equation of t (Expression)
Console.WriteLine(xf); // Prints t => ((value(cTreatmentGraphing.Program+<>c__DisplayClass0_0).a * t) + value(cTreatmentGraphing.Program+<>c__DisplayClass0_0).b)

Но это не дает значений, которые фактически использовались бы при вычислении лямбда-выражения, он просто возвращает его структура.

Можно ли определить значения во время выполнения и показать заполненные коэффициенты лямбда-выражения, и если да, то как мне go узнать об этом?

1 Ответ

2 голосов
/ 10 февраля 2020

Вы можете, если вы используете деревья выражений и напишите посетителю, который переписывает дерево, разрешая захваченные переменные; по сути, захваченные переменные реализованы в виде дерева поиска членов, которое в конечном итоге преобразуется в ConstantExpression, то есть контекст захвата; обратите внимание, что вам нужно делать это рекурсивно, потому что контексты захвата могут быть вложенными, если разные переменные захватываются в разных областях. Когда у вас есть информация о члене и целевой объект (захват-контекст) для каждого уровня, вы можете использовать отражение, чтобы оценить захваченную переменную:

using System;
using System.Linq.Expressions;
using System.Reflection;

class Dong
{
    public static implicit operator Dong(int i) => throw new InvalidOperationException("int");

    public static implicit operator Dong(decimal d) => throw new InvalidOperationException("decimal");

    static void Main()
    {
        decimal a = -0.5m;
        decimal b = 2m;
        Expression<Func<decimal, decimal>> f = t => a * t + b; // Linear equation of t
        Console.WriteLine(FunctionToString(f)); // Generate something like "-0.5 * t + 2"
    }

    static string FunctionToString(Expression f)
        => ConstantEvaluator.Instance.Visit(f).ToString();
    class ConstantEvaluator : ExpressionVisitor
    {
        public static ConstantEvaluator Instance { get; } = new ConstantEvaluator();
        private ConstantEvaluator() { }

        protected override Expression VisitMember(MemberExpression node)
        {
            var target = Visit(node.Expression); // applies recursion of nested contexts
            if (target is ConstantExpression c)
            {
                switch (node.Member)
                {
                    case FieldInfo field:
                        return Expression.Constant(field.GetValue(c.Value), field.FieldType);
                    case PropertyInfo prop:
                        return Expression.Constant(prop.GetValue(c.Value), prop.PropertyType);
                }
            }
            return node;
        }
    }
}

Вывод:

t => ((-0.5 * t) + 2)

Обратите внимание, что вы также можете использовать:

static string FunctionToString(LambdaExpression f)
    => ConstantEvaluator.Instance.Visit(f.Body).ToString();

, если вы не хотите лямбда-объявления; это дает вывод:

((-0.5 * t) + 2)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...