2010-01-12 8 views
9

Durante il mio lavoro con gli alberi di espressione per alcuni giorni, mi sono imbattuto in qualcosa che trovo difficile da capire; si spera che qualcuno sarà in grado di fare un po 'di luce qui.Come creare un'espressione <Func <dynamic, dynamic >> - Oppure è un bug?

Se si codifica Expression<Func<dynamic, dynamic>> expr1 = x => 2 * x; il compilatore si lamenterà e non si otterrà da nessuna parte. Tuttavia, lo sembra che sia che se si crea un'espressione di questo tipo tramite un metodo, il compilatore sembra essere soddisfatto e l'app risultante funziona. Questo non ha senso, quindi mi sto chiedendo cosa succede dietro le tende.

Suppongo che, sotto il cofano, l'espressione restituito da ConvertExpression è forse di tipo Expression<Func<object, object>>, che è un tipo valido, ma mi lascia perplesso che non posso usare Expression<Func<dynamic, dynamic>> tipo in una dichiarazione, eppure posso usarlo come il tipo di ritorno di un metodo. Guarda un esempio qui sotto.

Grazie mille!

public class ExpressionExample 
{ 
    public void Main() 
    { 
     // Doesn't compile: 
     //Expression<Func<dynamic, dynamic>> expr1 = x => 2 * x; 

     // Compiles and works - OK 
     Expression<Func<double, double>> expr2 = x => 2 * x; 
     Func<double, double> func2 = (Func<double, double>)expr2.Compile(); 
     Console.WriteLine(func2(5.0).ToString()); // Outputs 10 

     // Compiles and works - ??? This is the confusing block... 
     Expression<Func<dynamic, dynamic>> expr3 = ConvertExpression(expr2); 
     Func<dynamic, dynamic> func3 = (Func<dynamic, dynamic>)expr3.Compile(); 
     Console.WriteLine(func3(5.0).ToString()); // Outputs 10 

     // Side note: compiles and works: 
     Expression<Func<object, object>> expr4 = x => double.Parse(2.ToString()) * double.Parse(x.ToString()); 
     Func<object, object> func4 = (Func<object, object>)expr4.Compile(); 
     Console.WriteLine(func4(5.0).ToString()); // Outputs 10 
    } 

    private Expression<Func<dynamic, dynamic>> ConvertExpression<TInput, TOutput>(Expression<Func<TInput, TOutput>> expression) 
    { 
     Expression<Func<object, TInput>> convertToInput = value => (TInput)value; 
     // The following doesn't compile: var input = Expression.Parameter(typeof(dynamic), "input"); 

     var input = Expression.Parameter(typeof(object), "input");   

     Expression<Func<TOutput, dynamic>> convertToOutput = value => (dynamic)value; 

     var body = Expression.Invoke(convertToOutput, Expression.Invoke(expression, Expression.Invoke(convertToInput, input))); 
     var lambda = Expression.Lambda<Func<dynamic, dynamic>>(body, input); 

     return lambda; 
    } 
} 
+0

Dynamics in Expression Trees - Soluzione qui: [http://stackoverflow.com/questions/3562088/c-4-dynamic-in-expression-trees](http://stackoverflow.com/questions/3562088/c -4-dynamic-in-expression-trees) –

risposta

13

immagino che, sotto il cofano, l'espressione restituito da ConvertExpression è forse di tipo Expression<Func<object, object>>, che è un tipo valido

corretto.

Non riesco a utilizzare il tipo Expression<Func<dynamic, dynamic>> in una dichiarazione e tuttavia posso utilizzarlo come tipo di ritorno di un metodo.

Questa parte dell'istruzione non è corretta. Come si nota nel tuo esempio, è perfettamente legale usare quel tipo nella dichiarazione di una variabile locale.

Il bit che non è legale è l'esecuzione di un'operazione dinamica all'interno di una lambda che viene convertita in un tipo di albero di espressioni. Il tipo di albero dell'espressione specifica è irrilevante; ciò che conta è che l'operazione sia dinamica.

+0

Grazie mille per la risposta e il commento - Questo lo risolve! –

+0

@Eric fondamentalmente c'è (ancora?) Nessun supporto per le operazioni dinamiche all'interno di lambda. Ci sarà mai? – Maghis

+1

@Maghis: StackOverflow è un brutto posto per fare domande che richiedono una previsione del futuro. Non abbiamo piani per una funzione del genere ora; se lo faremo nei prossimi vent'anni, come dovrei saperlo? –

3

L'errore del compilatore ho avuto quando ho provato il codice era "l'errore CS1963: Un albero di espressione non può contenere un funzionamento dinamico". Ho cambiato la linea del problema in Expression<Func<dynamic, dynamic>> expr1 = x => x; (rimuovendo "l'operazione" dalla lambda) e ha funzionato! Quindi è consentito avere dinamiche nelle espressioni, ma in realtà non è possibile eseguire alcuna "operazione" su di esse. Non molto utile, lo so. Nei miei test, anche .ToString() conta come un'operazione.

+0

Grazie - Vedo lo stesso ... Ciò rende le cose ancora più bizzarre, penso - Quindi, se ci sono alcune operazioni, allora non si compila. Tuttavia, se non ce ne sono, viene compilato. Questo non contraddice la filosofia alla base del tipo dinamico di operazioni di risoluzione, invocazioni di metodi, ecc. In fase di esecuzione? –

+2

I codegen che generiamo per quelle operazioni dinamiche in fase di compilazione che implementa la semantica dinamica in fase di runtime sono estremamente complessi; sufficientemente complesso che non esiste un modo semplice per rappresentarlo in modo pulito in un albero di espressione. Fondamentalmente, la scelta si riduceva a "non permetterli negli alberi di espressione", o "spedisci in ritardo" o "spedisci il carrozzino". Abbiamo scelto il primo. Possiamo sempre aggiungere questo come una funzionalità nelle versioni future se c'è una domanda sufficiente; se riesci a descrivere gli avvincenti scenari utente che motivano questa funzione, ciò ti aiuterà quando sarà il momento di pianificare il funzionamento della funzione. –

+0

Se qualcuno apre un post di connessione su questo, voterò per questo. –

Problemi correlati