2008-09-10 11 views
11

Quiero redactar los resultados de dos expresiones de Linq. Existen en la formaCómo redacto expresiones de Linq existentes

Expression<Func<T, bool>> 

Así que los dos que quiero componer son esencialmente los delegados en un parámetro (de tipo T) que tanto devolver un valor booleano. El resultado que quisiera compilar sería la evaluación lógica de los booleanos. Yo probablemente implementarlo como un método de extensión por lo que mi sintaxis sería algo así como:

Expression<Func<User, bool>> expression1 = t => t.Name == "steve"; 
Expression<Func<User, bool>> expression2 = t => t.Age == 28; 
Expression<Func<User, bool>> composedExpression = expression1.And(expression2); 

Y más adelante en mi código Deseo evaluar la expresión compuesta

var user = new User(); 
bool evaluated = composedExpression.Compile().Invoke(user); 

He empujado por ahí con una pocas ideas diferentes, pero me temo que es más complejo de lo que esperaba. ¿Cómo se hace esto?

Respuesta

17

Aquí se muestra un ejemplo:

var user1 = new User {Name = "steve", Age = 28}; 
var user2 = new User {Name = "foobar", Age = 28}; 

Expression<Func<User, bool>> expression1 = t => t.Name == "steve"; 
Expression<Func<User, bool>> expression2 = t => t.Age == 28; 

var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>()); 

var result = Expression.Lambda<Func<User, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters); 

Console.WriteLine(result.Compile().Invoke(user1)); // true 
Console.WriteLine(result.Compile().Invoke(user2)); // false 

Puede volver a utilizar este código a través de métodos de extensión:

class User 
{ 
    public string Name { get; set; } 
    public int Age { get; set; } 
} 

public static class PredicateExtensions 
{ 
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1,Expression<Func<T, bool>> expression2) 
    { 
    InvocationExpression invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>()); 

    return Expression.Lambda<Func<T, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
    var user1 = new User {Name = "steve", Age = 28}; 
    var user2 = new User {Name = "foobar", Age = 28}; 

    Expression<Func<User, bool>> expression1 = t => t.Name == "steve"; 
    Expression<Func<User, bool>> expression2 = t => t.Age == 28; 

    var result = expression1.And(expression2); 

    Console.WriteLine(result.Compile().Invoke(user1)); 
    Console.WriteLine(result.Compile().Invoke(user2)); 
    } 
} 
1

¿Por qué no sólo tiene que utilizar Expression.And y tratar con el resultado BinaryExpression?

Expression<Func<T, bool>> expr1 = t => t.Name == "steve"; 
Expression<Func<T, bool>> expr2 = t => t.Age == 28; 
Expression composed = Expression.And(expr1.Body, expr2.Body); 

puede agrupar por supuesto esto en una lambda para obtener su firma deseada pero esto es caro y sólo debe hacerse vez, no varias veces:

Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(
    expr, Expression.Parameter(typeof(T), "t") 
); 

/Edit: se pueden por supuesto combinar lambdas de la siguiente manera pero esto implica compilaciones y funciones redundantes llama:

Expression<Func<string, bool>> z = t => expr1.Compile()(t) && expr2.Compile()(t); 

Maldita sea, el tiempo de inactividad mantenimiento. Tuve que escribir toda la publicación nuevamente. : -/

/EDITAR: aku tiene razón, sin embargo. Debe invocar expr2 por separado, de lo contrario, el compilador no encontrará la referencia del parámetro.

Cuestiones relacionadas