Функция или Предикат для ExpressionTree - PullRequest
3 голосов
/ 10 августа 2010

Допустим, у меня есть:

 Func<Customer,bool > a = (c) => c.fullName == "John";

Теперь я хочу преобразовать в expressiontree любой способ сделать это?

Я знаю, что я могу определить его с самого начала как дерево выражений, но у меня другая ситуация, потому что я должен сначала объединить некоторые лямбда-выражения, а затем передать его методу, который принимает дерево выражений, что приведет к ошибке времени компиляции!

пример:

        Func<Customer, bool> a = (c) => c.fullName == "John";
        Func<Customer, bool> b = (c) => c.LastName == "Smith";
        Func<Customer, bool> final = c => a(c) && b(c); 

теперь я хочу передать final методу, который принимает

ExpressionTree<Func<Customer,bool >>

выдает ошибку времени компиляции

спасибо заранее

Ответы [ 4 ]

5 голосов
/ 10 августа 2010

Вы не можете этого сделать. Переменная типа Func<...> представляет собой делегат , который в основном похож на указатель на область памяти, которая содержит скомпилированный код для лямбда-выражения. В .NET нет функциональности для преобразования уже скомпилированного кода обратно в дерево выражений.

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

Сводка: Если вам нужно полное дерево выражений для всех выражений, вам необходимо убедиться, что они являются деревьями выражений с самого начала. Как только вы скомпилируете его в делегат, дерево выражений будет потеряно.

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

Expression<Func<Customer, bool>> a = c => c.FullName == "John";
Expression<Func<Customer, bool>> b = c => c.LastName == "Smith";

var cp = Expression.Parameter(typeof(Customer), "c");

var ai = Expression.Invoke(a, cp);
var bi = Expression.Invoke(b, cp);

var final = Expression.Lambda<Func<Customer, bool>>(
    Expression.AndAlso(ai, bi), cp);

Конечно, здесь используется оператор AndAlso (&&); Вы также можете использовать OrElse для || и т. д.

2 голосов
/ 10 августа 2010

Что касается вашего пересмотренного вопроса, я думаю, что это будет работать:

Expression<Func<Customer, bool>> a = (c) => c.FullName == "John";
Expression<Func<Customer, bool>> b = (c) => c.LastName == "Smith";

var cp = Expression.Parameter(typeof(Customer), "c");

var ai = Expression.Invoke(a, cp);
var bi = Expression.Invoke(b, cp);

var final = Expression.Lambda<Func<Customer, bool>>(Expression.And(ai, bi), cp);
2 голосов
/ 10 августа 2010

Вы можете перейти от Expression к Func, но не наоборот.

Вы можете сделать это:

Expression<Func<Customer, bool>> exprA = (c) => c.fullName == "John";
Func<Customer, bool> funcA = exprA.Compile();

Но другого пути нет.

1 голос
/ 10 августа 2010

Вот решение, которое, я думаю, будет работать для вас.Я преобразую Func<TInput, TOutput> в Expression<Func<TInput, TOutput>>.

Он был основан в этом посте от ElegantCode

В этом примере я использовал Func:

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

namespace ConsoleApplication1
{
    class Program
    {
        public static bool Method(Expression<Func<int, bool>> predicate, int value)
        {
            return predicate.Compile()(value);
        }

        static void Main(string[] args)
        {
            Func<int, bool> testPredicate = n => n == 1;
            var output = ConvertFuncToExpression(testPredicate);
            Console.WriteLine(Method(output, 3));
            Console.WriteLine(Method(output, 1));
        }

        private static Expression<TDelegate> CreateExpression<TDelegate>(MethodBase method)
        {
            var delegateArguments = typeof(TDelegate).GetMethod("Invoke").GetParameters().Select((parameter, index) => Expression.Parameter(parameter.ParameterType, "param_" + index)).ToArray();
            if (delegateArguments.Count() != method.GetParameters().Count()) throw new InvalidOperationException("The number of parameters of the requested delegate does not match the number parameters of the specified method.");

            var argumentsTypes = method.GetGenericArguments();
            argumentsTypes = (argumentsTypes.Length > 0) ? argumentsTypes : null;
            var convertedArguments = method.GetParameters().Select((parameter, index) => Expression.Convert(delegateArguments[index], parameter.ParameterType)).ToArray();
            var call = Expression.Call(method.DeclaringType, method.Name, argumentsTypes, convertedArguments);

            var lambda = Expression.Lambda<TDelegate>(call, delegateArguments);
            return lambda;
        }

        private static Expression<Func<TIn1, TOut>> ConvertFuncToExpression<TIn1, TOut>(Func<TIn1, TOut> input)
        {
            MethodInfo method = input.Method;
            return CreateExpression<Func<TIn1, TOut>>(method);
        }
    }
}
...