2009-03-26 13 views
11

Sto cercando di creare una classe PredicateBuilder<T> che avvolge un Expression<Func<T, bool>> e fornisce alcuni metodi per costruire facilmente fino un'espressione con vari And e Or metodi. Ho pensato che sarebbe stato bello poter usare direttamente questo PredicateBuilder<T> come Expression<Func<T, bool>>, e ho pensato che si potesse fare con un metodo implicit operator.C#: operatore implicita e Metodi di estensione

ridotta versione della classe si presenta così:

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; 
    } 
} 

Poi, proprio come un test, ho questo metodo di estensione in una classe statica:

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

Nella mia testa, ho dovrebbe quindi essere in grado di fare questi:

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

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

Tuttavia nessuno di loro funziona. Per il primo, il metodo di estensione non è stato trovato. E per il secondo, si dice che

Il tipo di argomenti per il metodo 'ExtravagantExpressions.PredicateHelper.PrintExpression (System.Linq.Expressions.Expression>)' non possono essere dedotti dal loro utilizzo. Prova a specificare esplicitamente gli argomenti del tipo.

Così ho provato la seguente, che ha funzionato:

PredicateExtensions.PrintExpression<int>(p); 

Inoltre, questo funziona, naturalmente:

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

Ma sì ... perché non gli altri lavorano? Ho frainteso qualcosa su come funziona questa cosa implicit operator?

+3

Grazie per la pulizia! Continuo a scrivere extention invece di estensione ... Non so perché! Io solo ... non posso ... smettere ... = S – Svish

risposta

11

Questo non è specifico per i metodi di estensione. C# non convertirà implicitamente un oggetto in un altro tipo, a meno che non vi sia un indizio sul tipo di destinazione. Supponiamo quanto segue:

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() { ... } 
} 

Quale metodo ti aspetteresti di chiamare nella seguente dichiarazione?

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

Direi entrambi. No, suppongo che sia un punto, hehe. – Svish

+3

Mi aspetterei che "Foo() sia ambiguo: B.Foo() o C.Foo()" –

+1

@Anton: Ciò è possibile, ma complicherebbe la lingua e probabilmente nasconderebbe gli effetti collaterali. Dopotutto, come ti sentivi se un codice funzionante si interrompe improvvisamente quando definisci un nuovo operatore implicito in una classe :) È semplicemente più semplice forzare la dichiarazione esplicita del tipo ovunque. –

2

No, non è così, ma la deduzione del tipo del compilatore C# non è abbastanza potente per comprendere il codice e, in particolare, non considera gli operatori impliciti. Dovrai attenersi a Expression<Func<T,bool>> - perché non hai metodi di estensione come Or, And direttamente sulle espressioni?

+0

aha, ok, quindi tecnicamente dovrebbe funzionare, è solo che non cerca i metodi di estensione su cose di tipo operatore implicito? – Svish

+0

Direi che "idealmente" dovrebbe funzionare :) Solo così, non è abbastanza potente da sapere dove guardare. –

+0

Hai già quei metodi di estensione. Ma sto cercando di rendere le cose un po 'più facili da usare quando si costruiscono espressioni come questa. Non sono sicuro se riuscirò comunque: p – Svish

0

Come dice Anton, se si mettono i metodi di estensione direttamente su Expression<Func<...>> probabilmente funzionerebbe.

Altre spiegazioni ... niente di particolarmente intelligente, ma l'idea sarebbe che non si dispone di una classe PredicateBuilder di cui si creano istanze. Invece basta blocchi puramente statici:

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) 
    } 
} 

Queste due funzioni True e False svolgere il ruolo di vostro PredicateBuilder(bool) costruttore, e che ci si presumibilmente avere altri simili per i confronti primitive e così via, e poi operatori come And ti permetterebbe di collegare due espressioni insieme.

Tuttavia, si perde la possibilità di utilizzare i simboli operatore, che si sarebbero potuti usare con l'oggetto wrapper, e invece è necessario utilizzare i nomi dei metodi. Ho giocato con lo stesso tipo di approccio, e la cosa a cui torno sempre è che voglio essere in grado di definire gli operatori di estensione. Il team C# li ha considerati apparentemente per 3.0 (insieme alle proprietà di estensione) ma erano meno prioritari perché non avevano un ruolo negli obiettivi generali di Linq.

+0

Cosa intendi con "mettere il metodo di estensione direttamente su' Expression > '"? Non è quello che ho già fatto? O intendi usarlo su un 'Expression >' invece che su un 'PredicateBuilder '? (Sarebbe solo un uso normale, e ovviamente funziona) – Svish

+0

Vedere la spiegazione. –

+0

Non capisco ... – Svish

Problemi correlati