2011-08-28 38 views
14

Tôi đang học cây biểu thức trong C#.Cây biến thể và biểu thức cục bộ

Tôi đang bị mắc kẹt tại một thời gian:

string filterString = "ruby"; 
Expression<Func<string, bool>> expression = x => x == filterString; 

Làm thế nào tôi có thể xây dựng biểu thức này bằng cách mã? Không có mẫu làm thế nào để nắm bắt một biến địa phương. Cái này rất dễ dàng:

Expression<Func<string, bool>> expression = x => x == "ruby"; 

này sẽ là:

ParameterExpression stringParam = Expression.Parameter(typeof(string), "x"); 
Expression constant = Expression.Constant("ruby"); 
BinaryExpression equals = Expression.Equal(stringParam, constant); 
Expression<Func<string, bool>> lambda1 = 
    Expression.Lambda<Func<string, bool>>(
     equals, 
     new ParameterExpression[] { stringParam }); 

Các bản in debugger sau for (x => x == filterString):

{x => (x == giá trị (Predicate.Program + <> c__DisplayClass3) .filterString)}

Cảm ơn bạn đã tiết lộ một số ánh sáng về chủ đề này.

Trả lời

24

Ghi lại biến cục bộ thực sự được thực hiện bằng cách "nâng" biến cục bộ thành một biến thể dụ của lớp do trình biên dịch tạo ra. Trình biên dịch C# tạo ra một cá thể mới của lớp bổ sung vào thời điểm thích hợp và thay đổi bất kỳ quyền truy cập nào vào biến cục bộ thành một truy cập của biến cá thể trong cá thể có liên quan.

Vì vậy, cây biểu thức sau đó cần phải là truy cập trường trong cá thể - và bản thân cá thể được cung cấp qua ConstantExpression. Cách tiếp cận đơn giản nhất để làm việc cách tạo cây biểu thức thường tạo ra một thứ tương tự trong biểu thức lambda, sau đó xem mã được tạo trong Reflector, chuyển mức tối ưu hóa xuống sao cho Reflector không chuyển đổi nó thành lambda biểu thức.

+2

Cảm ơn. Các gợi ý để xem xét mã MSIL được tạo ra là rất hữu ích. – yonexbat

+3

Giống như 'var hoistedConstant = Expression.Property (Expression.Constant (new {Value = filterString})," Value ");' nên làm điều đó – Appetere

+1

@Appetere Còn 'Expression.Constant (filterString)' thì sao? Phải thừa nhận rằng, nó sẽ không phản ánh các thay đổi đối với biến, nhưng cũng không phải đề xuất của bạn. –

5

Mã này bao bọc biểu thức trong khối đóng để xử lý biến cục bộ dưới dạng hằng số.

string filterString = "ruby"; 

var filterStringParam = Expression.Parameter(typeof(string), "filterString"); 
var stringParam = Expression.Parameter(typeof(string), "x"); 

var block = Expression.Block(
// Add a local variable. 
new[] { filterStringParam }, 
// Assign a constant to the local variable: filterStringParam = filterString 
Expression.Assign(filterStringParam, Expression.Constant(filterString, typeof(string))), 
// Compare the parameter to the local variable 
Expression.Equal(stringParam, filterStringParam)); 

var x = Expression.Lambda<Func<string, bool>>(block, stringParam).Compile(); 
0

Một câu hỏi cũ nhưng tôi đến nó khi cố gắng làm điều gì đó biểu thức xây dựng tương tự cho LINQ-to-thực thể (L2E) Trong trường hợp đó bạn không thể sử dụng Expression.Block vì nó không thể được phân tích xuống SQL.

Dưới đây là một ví dụ rõ ràng theo câu trả lời của Jon sẽ hoạt động với L2E. Tạo một lớp helper để chứa các giá trị của bộ lọc:

class ExpressionScopedVariables 
{ 
    public String Value; 
} 

xây dựng cây như sau:

var scope = new ExpressionScopedVariables { Value = filterString}; 
var filterStringExp = Expression.Constant(scope); 
var getVariable = typeof(ExpressionScopedVariables).GetMember("Value")[0]; 
var access = Expression.MakeMemberAccess(filterStringExp, getVariable); 

Và sau đó thay thế hằng số trong các mã ban đầu với các biểu hiện truy cập thành viên:

BinaryExpression equals = Expression.Equal(stringParam, access); 
Expression<Func<string, bool>> lambda1 = 
    Expression.Lambda<Func<string, bool>>(
     equals, 
     new ParameterExpression[] { stringParam }); 
Các vấn đề liên quan