2011-12-14 14 views
7

Se volevo creare un albero di espressioni che chiamasse un metodo con un parametro out e quindi restituisse il valore out come risultato .. come potrei farlo?Parametri ByRef con alberi di espressione in C#

Quanto segue non funziona (genera un'eccezione di runtime), ma forse meglio dimostra quello che sto cercando di fare:

private delegate void MyDelegate(out int value); 
private static Func<int> Wrap(MyDelegate dele) 
{ 
    MethodInfo fn = dele.Method; 
    ParameterExpression result = ParameterExpression.Variable(typeof(int)); 
    BlockExpression block = BlockExpression.Block(
     typeof(int), // block result 
     Expression.Call(fn, result), // hopefully result is coerced to a reference 
     result); // return the variable 
    return Expression.Lambda<Func<int>>(block).Compile(); 
} 

private static void TestFunction(out int value) 
{ 
    value = 1; 
} 

private static void Test() 
{ 
    Debug.Assert(Wrap(TestFunction)() == 1); 
} 

So che questo può essere abbastanza facilmente risolto in IL prima (o anche senza compilazione di runtime a tutti), ma sfortunatamente questo fa parte di un processo di creazione di espressioni molto più grande ... quindi spero davvero che questa non sia una limitazione, in quanto una riscrittura completa sarebbe più di un po 'di dolore.

+1

Le funzioni lambda possono certamente chiamare metodi che hanno parametri 'ref' /' out' (come in questione), ciò che non possono fare è fare riferimento ai parametri 'ref' /' out' del metodo che li racchiude. – Mania

risposta

7

questo funziona per me:

private static Func<int> Wrap(MyDelegate dele) 
    { 
     var fn = dele.Method; 
     var result = ParameterExpression.Variable(typeof(int)); 
     var block = BlockExpression.Block(
      typeof(int), 
      new[] { result }, 
      new Expression[] 
      { 
       Expression.Call(fn, result), 
       result, 
      }); 
     return Expression.Lambda<Func<int>>(block).Compile(); 
    } 
-4

forse è solo me, ma io non vedo il punto di tutta la faccenda. Per realizzare ciò che stai cercando di fare, non hai davvero bisogno di scrivere tutte queste cose.

codice di esempio in una console app:

class Program 
    { 
     static void Main(string[] args) 
     { 
      var temp = Execute(DoSomething); 
      Console.Write(temp); 
      Console.Read(); 
     } 

     static int Execute(Func<int> methodToRun) 
     { 
      return methodToRun.Invoke(); 
     } 

     static int DoSomething() 
     { 
      return 1; 
     } 
    } 

Come si vede si ottiene gli stessi risultati in modo più conciso e pulito. Quello che penso stia perdendo è che Action, Action<> e Func<> sono tutti zucchero sintatico per delegate quindi non c'è bisogno di mescolare le 2 sintassi e non c'è bisogno di ricostruire l'intera espressione come si sta facendo.

+0

Il punto dell'esercizio è la generazione del codice di runtime. cioè, cosa succede se ti è stato passato un 'MethodInfo' completamente arbitrario, e volevi essere in grado di manipolare un po 'i risultati/parametri con la minima penalizzazione delle prestazioni possibili. Non è possibile chiamare direttamente il metodo, poiché non si conoscono i dettagli al momento della compilazione, è possibile utilizzare reflection per richiamare il metodo in fase di esecuzione, ma sarebbe molto lento .. Expression.Compile consente di eseguire un un po 'di lavoro in più la prima volta, ma crea un metodo che ti permetta di farlo ripetutamente a un costo minimo per le chiamate future. Questo è il punto;) – Mania

+0

Bene ora che lo spieghi, ma dal codice che hai scritto non sembra così. Ancora delegato MyDelegate (out int value) equivale a Func MyDelegate. Func è anche il tipo di ritorno di Wrap. Questo, per me, sembra inutile: il metodo ottiene un Func come parametro e restituisce lo stesso Func come risultato ... no, ancora non convincente :) –

+0

Leggi il bit immediatamente successivo al codice di esempio;), ho riconosciuto che runtime la generazione del codice non è richiesta qui, ma ha spiegato che fa parte di un problema più grande. Il codice di esempio serviva solo a dimostrare una versione minima e semplice del problema in questione - includere il codice completo richiederebbe diversi file .cs di grandi dimensioni. Ho preso in considerazione l'idea di fare il test case 'int.TryParse' (che restituisce un' bool' e un 'out int'), e richiedendo all'espressione di combinarli entrambi in un' Tuple' .. ma che sembrava inutilmente complicato. (E potrebbe anche essere risolto senza generazione di codice di runtime) – Mania

Problemi correlati