Cập nhật Câu hỏi tiếp theo XuốngBiểu/Tuyên Bố
Tôi đã thử nghiệm với cây biểu hiện trong .NET 4 để tạo ra mã trong thời gian chạy và tôi đã cố gắng để thực hiện báo cáo kết quả foreach
bằng cách xây dựng một cây biểu .
Cuối cùng, khái niệm sẽ có thể tạo ra một đại biểu mà thực hiện điều này:
Action<IEnumerable<int>> action = source =>
{
var enumerator = source.GetEnumerator();
while(enumerator.MoveNext())
{
var i = enumerator.Current;
// the body of the foreach that I don't currently have yet
}
}
tôi đã đi lên với phương pháp helper sau đó tạo ra một BlockExpression từ một IEnumerable:
public static BlockExpression ForEachExpr<T>(this IEnumerable<T> source, string collectionName, string itemName)
{
var item = Expression.Variable(typeof(T), itemName);
var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");
var param = Expression.Parameter(typeof(IEnumerable<T>), collectionName);
var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetMethod("MoveNext"));
var assignToEnum = Expression.Assign(enumerator, Expression.Call(param, typeof(IEnumerable<T>).GetMethod("GetEnumerator")));
var assignCurrent = Expression.Assign(item, Expression.Property(enumerator, "Current"));
var @break = Expression.Label();
var @foreach = Expression.Block(
assignToEnum,
Expression.Loop(
Expression.IfThenElse(
Expression.NotEqual(doMoveNext, Expression.Constant(false)),
assignCurrent
, Expression.Break(@break))
,@break)
);
return @foreach;
}
Các mã sau đây:
var ints = new List<int> { 1, 2, 3, 4 };
var expr = ints.ForEachExpr("ints", "i");
var lambda = Expression.Lambda<Action<IEnumerable<int>>>(expr, Expression.Parameter(typeof(IEnumerable<int>), "ints"));
Tạo cây biểu thức này:
.Lambda #Lambda1<System.Action`1[System.Collections.Generic.IEnumerable`1[System.Int32]]>(System.Collections.Generic.IEnumerable`1[System.Int32] $ints)
{
.Block() {
$enumerator = .Call $ints.GetEnumerator();
.Loop {
.If (.Call $enumerator.MoveNext() != False) {
$i = $enumerator.Current
} .Else {
.Break #Label1 { }
}
}
.LabelTarget #Label1:
}
}
này có vẻ là OK, nhưng gọi Compile
trên mà kết quả biểu hiện trong một ngoại lệ:
"variable 'enumerator' of type 'System.Collections.Generic.IEnumerator`1[System.Int32]' referenced from scope '', but it is not defined"
Không phải tôi định nghĩa nó ở đây:
var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");
?
Tất nhiên, ví dụ ở đây được giả tạo và chưa sử dụng thực tế, nhưng tôi đang cố gắng thu thập các cây biểu thức có thân, để kết hợp động chúng vào thời gian chạy trong tương lai.
EDIT: vấn đề ban đầu của tôi đã được giải quyết bởi Alexandra, cảm ơn! Tất nhiên, tôi đã gặp phải vấn đề tiếp theo ngay bây giờ. Tôi đã khai báo một số BlockExpression
có một biến trong đó. Bên trong biểu thức đó, tôi muốn một biểu thức khác tham chiếu biến đó. Nhưng tôi không có một tham chiếu thực sự cho biến đó, chỉ là tên của nó, bởi vì biểu thức được cung cấp bên ngoài.
var param = Expression.Variable(typeof(IEnumerable<T>), "something");
var block = Expression.Block(
new [] { param },
body
);
Biến body
được thông qua tại bên ngoài và không có tài liệu tham khảo trực tiếp đến param
, nhưng không biết tên của các biến trong biểu thức ("something"
). Nó trông giống như thế này:
var body = Expression.Call(typeof(Console).GetMethod("WriteLine",new[] { typeof(bool) }),
Expression.Equal(Expression.Parameter(typeof(IEnumerable<int>), "something"), Expression.Constant(null)));
Đây là "mã" rằng đây tạo:
.Lambda #Lambda1<System.Action`1[System.Collections.Generic.IEnumerable`1[System.Int32]]>(System.Collections.Generic.IEnumerable`1[System.Int32] $something)
{
.Block(System.Collections.Generic.IEnumerable`1[System.Int32] $something) {
.Call System.Console.WriteLine($something== null)
}
}
Tuy nhiên, nó không biên dịch. Với cùng một lỗi như trước.
TLDR: Làm cách nào để tham chiếu biến bằng số nhận dạng trong cây biểu thức?
Tôi nghĩ bạn không thể làm điều này. Tên mà bạn cung cấp cho các tham số bên trong cây biểu thức giống như tên thân thiện hơn. Bạn thực sự không cần chúng, bạn có thể tạo một tham số không có tên và hệ thống sẽ tạo ra một cái gì đó cho bạn. Nhưng điều này là nhiều hơn cho mục đích gỡ lỗi hơn cho bất cứ điều gì khác. Vì vậy, bạn chỉ cần tạo hai tham số có cùng tên chứ không phải tham số và tham chiếu đến nó. Tôi sẽ đưa ví dụ của bạn đến nhóm DLR và hỏi xem đó có phải là lỗi hay không mà bạn có thể tạo hai thông số có cùng tên như thế này. Nhưng tôi có thể nhận được câu trả lời chỉ sau những ngày nghỉ. –
Hm, do đó, không thể tự động soạn một đại biểu bằng cách thêm các bit và phần riêng biệt lại vào một cây biểu thức? Mục tiêu cuối cùng của tôi là tạo mã bằng cách sử dụng một thuật toán tiến hóa và cho điều đó, tôi thực sự cần phải có khả năng tham chiếu các biến được tạo ra trong phạm vi bên ngoài. Cảm ơn bạn đã giúp đỡ :) – JulianR
Tôi không nói rằng :-) Tất nhiên bạn có thể tạo một đại biểu hoặc một phương pháp tĩnh với cây biểu thức (tôi thậm chí có một bài đăng blog về điều đó: http://blogs.msdn.com /csharpfaq/archive/2009/09/14/generating-dynamic-methods-with-expression-trees-in-visual-studio-2010.aspx) Nhưng bạn có thể cần phải cấu trúc lại đoạn mã chính xác này, để "cơ thể" "nên có tham chiếu thực sự tới" param "không chỉ là tên chuỗi. –