2011-12-14 42 views
7

Nếu tôi muốn tạo một cây biểu thức được gọi là phương thức có tham số out và sau đó trả về giá trị out .. làm cách nào để tôi thực hiện?Các tham số ByRef với cây biểu thức trong C#

Sau đây không hoạt động (ném một ngoại lệ thời gian chạy), nhưng có lẽ tốt nhất chứng minh những gì tôi đang cố gắng để làm:

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

Tôi biết điều này có thể được khá dễ dàng giải quyết trong tình trạng thô IL (hoặc thực sự không biên dịch thời gian ở tất cả), nhưng tiếc là đây là một phần của một quá trình xây dựng biểu thức lớn hơn nhiều ... vì vậy tôi thực sự hy vọng đây không phải là một hạn chế, như viết lại hoàn toàn sẽ là nhiều hơn một chút đau.

+1

Hàm Lambda chắc chắn có thể gọi các phương thức có tham số 'ref' /' out' (như trong câu hỏi), những gì chúng không thể làm là tham khảo các tham số 'ref' /' out' của phương thức kèm theo. – Mania

Trả lời

7

này làm việc cho tôi:

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

Có lẽ nó chỉ là tôi, nhưng tôi không thực sự nhìn thấy điểm của toàn bộ điều. Để thực hiện những gì bạn đang cố gắng làm, bạn không thực sự cần phải viết tất cả những thứ đó.

Mẫu mã trong một ứng dụng giao diện điều khiển:

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

Như bạn thấy nó được cho bạn những kết quả tương tự một cách ngắn gọn hơn và sạch sẽ. Những gì tôi nghĩ rằng bạn đã bỏ lỡ là Action, Action<>Func<> là tất cả đường sintactic cho delegate do đó không cần phải trộn 2 cú pháp và không cần phải tạo lại toàn bộ biểu thức như bạn đang làm.

+0

Điểm của bài tập là tạo mã thời gian chạy. tức là, nếu bạn được thông qua một 'MethodInfo' hoàn toàn tùy ý, và muốn có thể kết thúc các kết quả/tham số một chút với hiệu suất tối thiểu có thể có. Bạn không thể gọi phương thức trực tiếp, vì bạn không biết chi tiết của nó tại thời gian biên dịch, bạn có thể sử dụng sự phản chiếu để gọi phương thức trong thời gian chạy, nhưng điều đó sẽ rất chậm .. Expression.Compile cho phép bạn thực hiện bit làm việc nhiều hơn lần đầu tiên, nhưng tạo ra một phương pháp cho phép bạn làm điều đó nhiều lần với chi phí tối thiểu cho các lời gọi trong tương lai. Đây là điểm;) – Mania

+0

Bây giờ bạn đã giải thích nó, nhưng từ mã bạn viết nó không xuất hiện như thế. Vẫn là đại biểu MyDelegate (giá trị int) tương đương với Func MyDelegate. Func cũng là kiểu trả về của Gói. Điều đó, với tôi, có vẻ vô nghĩa: phương pháp nhận được một Func làm tham số và xuất ra cùng một Func như là kết quả ... không, vẫn không thuyết phục :) –

+0

Đọc bit ngay sau mã mẫu;), tôi thừa nhận rằng thời gian chạy tạo mã không cần thiết ở đây, nhưng giải thích rằng đó là một phần của một vấn đề lớn hơn. Mã mẫu chỉ được cung cấp để chứng minh một phiên bản đơn giản, tối thiểu của sự cố trong tầm tay - để bao gồm toàn bộ mã sẽ yêu cầu một số tệp .cs lớn. Tôi đã xem xét việc tạo ra test 'int.TryParse' (trả về một' bool' và 'out int'), và yêu cầu Expression kết hợp cả hai thành' Tuple' .. nhưng điều đó dường như không cần thiết phức tạp. (Và cũng có thể được giải quyết mà không cần tạo mã thời gian chạy) – Mania

Các vấn đề liên quan