2011-12-14 2 views
7

Если мне захотелось создать дерево выражений, которое вызвало метод с параметром out, а затем вернуло значение out .. как бы я это сделал?Параметры ByRef с деревьями выражений в C#

Следующая не работает (выдает исключение времени выполнения), но, возможно, лучше всего демонстрирует то, что я пытаюсь сделать:

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

Я знаю, это может быть довольно легко решается в сыром IL (или даже без компиляция во время выполнения), но, к сожалению, это часть гораздо более крупного процесса построения выражения ... поэтому я действительно надеюсь, что это не ограничение, так как полная переписывание будет более чем немного боли.

+1

Лямбда-функции, безусловно, могут вызывать методы, которые имеют параметры 'ref' /' out' (как в вопросе), то, что они не могут сделать, относятся к параметрам 'ref' /' out' в приложении. – Mania

ответ

7

Это работает для меня:

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

Может быть, это только мне, но я не вижу смысла целого. Чтобы выполнить то, что вы пытаетесь сделать, вам не нужно писать все это.

Пример кода в приложении консоли:

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

Как вы видите, он получает вас одни и те же результаты в более краткой и чистым способом. Я думаю, что вы отсутствовали, так это то, что Action, Action<> и Func<> - все синтаксический сахар для delegate, поэтому нет необходимости смешивать 2 синтаксиса и не нужно восстанавливать все выражение, как вы это делаете.

+0

Точка упражнения - это генерация кода времени выполнения. то есть, если вам передали полностью произвольный 'MethodInfo', и он хотел бы немного закрутить результаты/параметры с минимальным возможным штрафом за производительность. Вы не можете вызвать метод напрямую, так как вы не знаете его детали во время компиляции, вы можете использовать отражение для вызова метода во время выполнения, но это будет очень медленно. Expression.Compile позволяет вам делать бит больше работы в первый раз, но создайте метод, который позволит вам делать это повторно с минимальными затратами для будущих вызовов. Это точка;) – Mania

+0

Ну, теперь, когда вы это объясните, но из кода, который вы написали, это не так. Тем не менее этот делегат MyDelegate (out int value) эквивалентен Func MyDelegate. Func также является возвратным типом Wrap. Для меня это кажется бессмысленным: метод получает параметр Func и выводит тот же Func как результат ... нет, все еще не убедительно :) –

+0

Прочитайте бит сразу после образца кода;), я признал, что время выполнения генерация кода здесь не требуется, но объясняется, что это часть более крупной проблемы. Образец кода служил только для демонстрации минимальной простой версии проблемы - для включения полного кода потребуется несколько больших файлов .cs. Я действительно рассматривал создание тестового примера 'int.TryParse' (который возвращает' bool' и 'out int') и требует, чтобы Expression объединил их обоих в' Tuple' .. но это казалось излишне сложным. (И также может быть решена без генерации кода времени исполнения) – Mania

Смежные вопросы