Деревья выражений / операторов
Обновленный вопрос дальше вниз '
я экспериментировал с деревьями выражений в .NET 4 для генерации кода во время выполнения, и ямы пытались реализоватьforeach
утверждение путем построения дерева выражений.
В конце выражение должно быть в состоянии генерировать делегата, который делает это:
Action 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
}
}
Мы придумали следующий вспомогательный метод, который генерирует BlockExpression из IEnumerable:
public static BlockExpression ForEachExpr(this IEnumerable source, string collectionName, string itemName)
{
var item = Expression.Variable(typeof(T), itemName);
var enumerator = Expression.Variable(typeof(IEnumerator), "enumerator");
var param = Expression.Parameter(typeof(IEnumerable), collectionName);
var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetMethod("MoveNext"));
var assignToEnum = Expression.Assign(enumerator, Expression.Call(param, typeof(IEnumerable).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;
}
Следующий код:
var ints = new List { 1, 2, 3, 4 };
var expr = ints.ForEachExpr("ints", "i");
var lambda = Expression.Lambda(expr, Expression.Parameter(typeof(IEnumerable), "ints"));
Создает это дерево выражений:
.Lambda #Lambda1(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:
}
}
Это вроде бы нормально, но звонюCompile
в этом выражении выдается исключение:
"variable 'enumerator' of type 'System.Collections.Generic.IEnumerator`1[System.Int32]' referenced from scope '', but it is not defined"
Didn»Я определяю это здесь:?
var enumerator = Expression.Variable(typeof(IEnumerator), "enumerator");
Конечно, пример здесь надуман и непока не имеет практического применения, но яЯ пытаюсь получить представление о деревьях выражений, которые имеют тела, чтобы динамически объединять их во время выполнения в будущем.
РЕДАКТИРОВАТЬ: Моя первоначальная проблема была решена Александрой, спасибо! Конечно я'мы столкнулись с следующей проблемой сейчас. Я'мы объявилиBlockExpression
в котором есть переменная. Внутри этого выражения я хочу другое выражение, которое ссылается на эту переменную. Но я неФактическая ссылка на эту переменную, только ее имя, потому что выражение предоставляется извне.
var param = Expression.Variable(typeof(IEnumerable), "something");
var block = Expression.Block(
new [] { param },
body
);
body
переменная передается извне и не имеет прямой ссылки наparam
, но знает имя переменной в выражении ("something"
). Это выглядит так:
var body = Expression.Call(typeof(Console).GetMethod("WriteLine",new[] { typeof(bool) }),
Expression.Equal(Expression.Parameter(typeof(IEnumerable), "something"), Expression.Constant(null)));
Это "код" что это порождает:
.Lambda #Lambda1(System.Collections.Generic.IEnumerable`1[System.Int32] $something)
{
.Block(System.Collections.Generic.IEnumerable`1[System.Int32] $something) {
.Call System.Console.WriteLine($something== null)
}
}
Тем не менее, это нет компилировать. С той же ошибкой, что и раньше.
TLDR: Как мне ссылаться на переменную по идентификатору в дереве выражений?