2015-12-14 13 views
6

Tôi đang thực hiện Func ->Biểu thức ->Func chuyển đổi. Nó hoạt động tốt nếu tôi tạo Func <>() từ phương thức (ví dụ đầu tiên dưới đây) tuy nhiên nếu tôi tạo hàm bằng cách sử dụng cây biểu thức (ví dụ thứ 2), nó không thành công với NullReferenceException khi truy cập func2.Method.DeclaringType. Tên đầy đủ. Và điều này là do DeclaringType is null. (NJection sử dụng sự phản chiếu vì vậy tôi nghĩ đó là lý do tại sao nó cần DeclaringType.)Có cách nào để đặt 'DeclaringType' trong cây biểu thức không?

Làm thế nào tôi có thể điền vào loại DeclaringType cho Func <> được tạo ra bằng cách biên dịch một cây biểu hiện? (có lẽ nó không thể?) DeclaringType được thiết lập trong ví dụ đầu tiên.

Sử dụng một Func <> từ một phương pháp ... (hoạt động tốt)

// Build a Func<> 
Func<int, int> add = Add; 
// Convert it to an Expression using NJection Library 
Expression<Func<int, int>> expr = ToExpr<Func<int, int>>(add); 
// Convert it back to a Func<> 
Func < int, int> func = expr.Compile(); 
// Run the Func<> 
int result = func(100); 

Sử dụng một cây biểu thức (không hoạt động) ...

// Build a Func<> using an Expression Tree 
ParameterExpression numParam = Expression.Parameter(typeof(int)); 
ConstantExpression five = Expression.Constant(5, typeof(int)); 
BinaryExpression numAddFive = Expression.Add(numParam, five); 
Func<int, int> func2 = 
    Expression.Lambda<Func<int, int>>(
     numAddFive, 
     new ParameterExpression[] { numParam }).Compile(); 
// Convert to an Expression using NJection (EXCEPTION ON NEXT LINE) 
// ToExpr is trying to access func2.Method.DeclaringType.FullName(DeclaringType is null) 
Expression<Func<int, int>> exp2 = ToExpr<Func<int, int>>(func2); 
// Convert it back to a Func<> 
Func<int, int> func2b = exp2.Compile(); 
// Run the Func<> 
int result2 = func2b(100); 
+2

Bạn đang cố gắng đạt được điều gì với 'ToExpr > (func2)'? Bạn muốn quay trở lại loại biểu hiện nào? – torvin

+0

Tôi vẫn đang học LINQ/labda/etc. Mục tiêu của tôi là đơn giản hóa một chương trình biểu thức biểu thức (hoặc cây). Có lẽ tôi có thể có trình biên dịch biên dịch một biểu thức để mã sau đó lấy mã đó và chuyển nó trở lại một cây biểu thức. Tôi đoán cây biểu thức kết quả sẽ là một phiên bản rút gọn của bản gốc. Vì vậy, nếu tôi có một cây biểu thức của một + a + a + 2 + 4 + 3 thì tôi biên dịch nó và chuyển đổi mã đó trở lại thành một biểu thức tôi sẽ nhận được (3 * a) +9. (Tôi đoán) Tôi đã nói Func -> Expr -> Fun nhưng mục tiêu thực sự là Expr -> Func (code) -> Expr. Nếu tôi có thể làm việc đầu tiên thì tôi nghĩ tôi có thể làm sau. – Sunsetquest

+0

Tại sao bạn muốn đơn giản hóa biểu thức? Nếu bạn chỉ cần biên dịch và chạy sau đó, trình biên dịch vẫn sẽ làm tất cả các tối ưu hóa cho nó, vì vậy đừng bận tâm. Nếu bạn đang cố gắng sử dụng trình biên dịch để đơn giản hóa các biểu thức cho bạn cho một số nhiệm vụ khác - bạn có thể sẽ không đạt được nhiều, vì ý tưởng của bạn về 'đơn giản' có thể khá khác với trình biên dịch. – torvin

Trả lời

3

Tôi không biết thư viện NJection được sử dụng vì trang web của họ không hoạt động và không có sẵn mã nguồn tại Codeplex.

Nếu bạn chỉ cần nhận được Expression mà bạn có thể biên dịch lại cho hàm, bạn có thể tự tạo. Ví dụ. như thế này:

static Expression<T> ToExpr<T>(T func) 
{ 
    var type = typeof(T); 
    var method = type.GetMethod("Invoke"); // Delegate.Invoke() has parameters types matching delegate parameters types 
    var pars = method.GetParameters() 
     .Select(pi => Expression.Parameter(pi.ParameterType)) 
     .ToArray(); 

    return Expression.Lambda<T>(
     Expression.Call(Expression.Constant(func), method, pars), 
     pars 
    ); 
} 

Với mã này ToExpr, mã trên của bạn biên dịch và chạy mà không gặp sự cố.

+0

Thư viện có tại đây https://github.com/sagifogel/NJection.LambdaConverter. Tôi đã sao chép phương thức "ExecuteLambda" sau đó xóa ".compile()" cuối cùng và lưu nó dưới dạng phương thức 'ToExpr'. Phương thức của bạn làm việc cho mã ví dụ của tôi nhưng tôi nhận thấy 'expr.body' có một 'Invoke' và không phải là 'block' với các biểu thức trong nó giống như 'NJection' tạo ra. Tôi có thể không biết tôi đang nói về cái gì. =) Đó là khá mát mẻ, bạn tạo ra một chức năng mà làm việc cho các dữ liệu thử nghiệm! – Sunsetquest

+0

Tôi vẫn không chắc chắn những gì NJection làm, nhưng nếu nó thực sự decompiles các đại biểu, sau đó câu trả lời của tôi sẽ không giúp bạn. Ngoài ra, để đơn giản hóa cây biểu thức, bạn có thể sử dụng ['PartialEvaluatingExpressionTreeVisitor'] của relinq (http://www.nudoq.org/#!/Packages/Remotion.Linq/Remotion.Linq/PartialEvaluatingExpressionTreeVisitor). Nó có thể sẽ không cung cấp cho bạn 'a + a + a' ->' 3 * a' chuyển đổi, nhưng nó chắc chắn sẽ chuyển đổi '2 + 4 + 3' thành' 9'. – torvin

+0

Họ đã có một trang web và nó đã được lên khoảng một tháng trước nhưng có vẻ như tên miền cha của họ đã hết hạn. Về cơ bản nó là một công cụ cho phép các biểu thức được lưu dưới dạng xml nhưng nó có một hàm này có thể biến một func thành một expr. Tôi nhìn vào relinq nhưng không thể tìm thấy bất kỳ ví dụ mã cho PartialEvaluatingExpressionTreeVisitor. Tôi sẽ thử sử dụng nó. Cảm ơn bạn cho những ý tưởng và tất cả sự giúp đỡ của bạn. Nó đã giúp tôi. – Sunsetquest

2

Tôi đoán không .

Khi bạn biên dịch biểu thức thành Func thực thi, đó là phương pháp động. Bạn có thể thấy điều này nếu bạn gọi func2.Method.GetType(). Điều đó trả về type DynamicMethod luôn có null làm Declaring Type.

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