Переименовать параметр в лямбда-выражении C# - PullRequest
1 голос
/ 14 февраля 2020

У меня есть следующая проблема в моем лямбда-выражении: мне нужно переименовать свойство, потому что оно будет передано от сущности к сущности. Другими словами: мне нужно использовать одно и то же выражение в нескольких запросах в разных сущностях.

Например:

var expr = x => x.Id == converterId

Чтобы быть

var expr = x => x.ConverterId == converterId

I пытался сделать следующее

var oldParam = expr.Parameters[0];
var newParam = Expression.Parameter(oldParam.Type, "ConverterId");

Этот код заменяет x not Id`

1 Ответ

0 голосов
/ 14 февраля 2020

Это не тривиально, но может быть сделано мое написание (подклассы) ExpressionVisitor и переопределение VisitMember, делая подстановку другого Expression.Property, но используя исходное целевое выражение (из выражения в исходная лямбда)

Однако, для простых случаев, вероятно, легче забыть об этом и просто построить дерево выражений вручную из первых принципов, а не использовать лямбду.


Ниже показаны оба подхода:

using System;
using System.Linq.Expressions;

static class P
{
    static void Main()
    {
        // the compiler-generated expression-tree from the question
        Console.WriteLine(Baseline(42));

        // build our own epression trees manually
        Console.WriteLine(ByName(42, nameof(Foo.Id)));
        Console.WriteLine(ByName(42, nameof(Foo.ConverterId)));

        // take the compiler-generated expression tree, and rewrite it with a visitor
        Console.WriteLine(Convert(Baseline(42), nameof(Foo.Id), nameof(Foo.ConverterId)));
    }
    static Expression<Func<Foo, bool>> Baseline(int converterId)
    {
        // note this uses a "captured variable", so the output
        // looks uglier than you might expect
        return x => x.Id == converterId;
    }
    static Expression<Func<Foo, bool>> ByName(int converterId, string propertyOrFieldName)
    {
        var p = Expression.Parameter(typeof(Foo), "x");
        var body = Expression.Equal(
            Expression.PropertyOrField(p, propertyOrFieldName),
            Expression.Constant(converterId, typeof(int))
            );
        return Expression.Lambda<Func<Foo, bool>>(body, p);
    }

    static Expression<Func<Foo, bool>> Convert(
        Expression<Func<Foo, bool>> lambda, string from, string to)
    {
        var visitor = new ConversionVisitor(from, to);
        return (Expression<Func<Foo, bool>>)visitor.Visit(lambda);
    }
    class ConversionVisitor : ExpressionVisitor
    {
        private readonly string _from, _to;
        public ConversionVisitor(string from, string to)
        {
            _from = from;
            _to = to;
        }
        protected override Expression VisitMember(MemberExpression node)
        {
            if(node.Member.Name == _from)
            {
                return Expression.PropertyOrField(
                    node.Expression, _to);
            }
            return base.VisitMember(node);
        }

    }
}
class Foo
{
    public int Id { get; set; }

    public int ConverterId { get; set; }
}
...