2009-03-26 14 views
11

Estoy tratando de crear una clase PredicateBuilder<T> que envuelve un Expression<Func<T, bool>> y proporciona algunos métodos para crear fácilmente una expresión con varios métodos And y Or. Pensé que sería genial si pudiera usar este PredicateBuilder<T> como un Expression<Func<T, bool>> directamente, y pensé que esto podría hacerse teniendo una cosa de método implicit operator.C#: operador implícito y métodos de extensión

simplificada versión de la clase tiene el siguiente aspecto:

class PredicateBuilder<T> 
{ 
    public Expression<Func<T, bool>> Predicate { get; protected set; } 

    public PredicateBuilder(bool initialPredicate) 
    { 
     Predicate = initialPredicate 
      ? (Expression<Func<T, bool>>) (x => true) 
      : x => false; 
    } 

    public static implicit operator Expression<Func<T, bool>>(
     PredicateBuilder<T> expressionBuilder) 
    { 
     return expressionBuilder.Predicate; 
    } 
} 

Entonces, como una prueba, tengo este método de extensión en una clase estática:

public static void PrintExpression<T>(this Expression<Func<T, bool>> expression) 
{ 
    Console.WriteLine(expression); 
} 

En mi cabeza, a continuación, debería ser capaz de hacer esto:

var p = new PredicateBuilder<int>(true); 

p.PrintExpression(); 
PredicateExtensions.PrintExpression(p); 

Sin embargo ninguno de ellos funciona. Para el primero, el método de extensión no se encuentra. Y para el segundo, se dice que

El tipo de argumentos para el método 'ExtravagantExpressions.PredicateHelper.PrintExpression (System.Linq.Expressions.Expression>)' no se pueden deducir a partir del uso. Intente especificar los argumentos de tipo explícitamente.

así que he intentado lo siguiente, que trabajó:

PredicateExtensions.PrintExpression<int>(p); 

Además, esto funciona, por supuesto:

((Expression<Func<int, bool>>) p).PrintExpression(); 

Pero sí ... ¿por qué no los otros trabajan? ¿He entendido mal acerca de cómo funciona esta cosa implicit operator?

+3

Gracias por la limpieza! Sigo escribiendo extensión en lugar de extensión ... ¡No sé por qué! Yo solo ... no puedo ... parar ... = S – Svish

Respuesta

11

Esto no es específico de los métodos de extensión. C# no lanzará implícitamente un objeto a otro tipo a menos que haya una pista sobre el tipo de objetivo. Suponga lo siguiente:

class A { 
    public static implicit operator B(A obj) { ... } 
    public static implicit operator C(A obj) { ... } 
} 

class B { 
    public void Foo() { ... } 
} 

class C { 
    public void Foo() { ... } 
} 

¿Qué método esperaría recibir en la siguiente declaración?

new A().Foo(); // B.Foo? C.Foo? 
+0

Yo diría que ambas cosas. Nah, adivina que es un punto, jeje. – Svish

+3

Supongo que "Foo() es ambiguo: B.Foo() o C.Foo()" –

+1

@ Anton: Eso es posible, pero complicaría el lenguaje y posiblemente ocultaría los efectos secundarios. Y, después de todo, ¿cómo te sentirías si un código de trabajo se rompe repentinamente cuando defines un nuevo operador implícito en una clase? Simplemente es más simple forzar la declaración de tipo explícito en todas partes. –

2

No, no lo ha hecho, pero la deducción del tipo del compilador de C# no es lo suficientemente potente como para comprender su código, y en particular, no se trata de operadores implícitos. Tendrá que seguir con Expression<Func<T,bool>> - ¿por qué no tener métodos de extensión como Or, And directamente en las expresiones?

+0

aha, ok, entonces técnicamente debería funcionar, ¿es solo que no busca métodos de extensión en cosas implícitas del tipo de operador? – Svish

+0

Yo diría que "idealmente" debería funcionar :) De la misma manera, no es lo suficientemente potente como para saber dónde buscar. –

+0

Ya tienes esos métodos de extensión. Pero trato de hacer las cosas un poco más fáciles de trabajar cuando construyo expresiones como esta. Sin embargo, no estoy seguro si tendré éxito: p – Svish

0

Como Anton dice, si pones los métodos de extensión directamente en Expression<Func<...>>, probablemente funcione.

Más explicación ... nada especialmente inteligente, pero la idea sería que no tiene una clase PredicateBuilder que crea instancias de. En su lugar sólo hay bloques de construcción puramente estáticas:

public static class Predicates 
{ 
    public static Expression<Func<T, bool>> True<T>() 
    { 
     return x => true; 
    } 

    public static Expression<Func<T, bool>> False<T>() 
    { 
     return x => false; 
    } 

    public static Expression<Func<T, bool>> And<T>(
     this Expression<Func<T, bool>> left, 
     Expression<Func<T, bool>> right) 
    { 
     return ... // returns equivalent of (left && right) 
    } 
} 

Esas dos funciones True y False jugar el papel de su PredicateBuilder(bool) constructor, y que presumiblemente tendríamos otras similares para las comparaciones primitivos y así sucesivamente, y después los operadores como And te dejaría conectar dos expresiones juntas.

Sin embargo, luego pierde la capacidad de utilizar símbolos de operador, que podría haber utilizado con su objeto envoltorio, y en su lugar debe usar nombres de método. He estado jugando con el mismo tipo de enfoques, y a lo que siempre vuelvo es a que quiero poder definir operadores de extensión. El equipo de C# aparentemente consideró esto para 3.0 (junto con las propiedades de extensión) pero fueron de menor prioridad porque no formaron parte de los objetivos generales de Linq.

+0

¿Qué quiere decir con "poner el método de extensión directamente en' Expresión > '"? ¿No es eso lo que ya hice? ¿O quiere decir usarlo en un 'Expression >' en lugar de en un 'PredicateBuilder '? (Eso sería solo un uso normal, y funciona por supuesto) – Svish

+0

Ver explicación. –

+0

No lo entiendo ... – Svish

Cuestiones relacionadas