我在你的问题中迷失了方向(如果我解释错误,请告诉我,我会再深入探讨),但我think这就是你所追求的:
public static Expression ForEach(Expression collection, ParameterExpression loopVar, Expression loopContent)
{
var elementType = loopVar.Type;
var enumerableType = typeof(IEnumerable<>).MakeGenericType(elementType);
var enumeratorType = typeof(IEnumerator<>).MakeGenericType(elementType);
var enumeratorVar = Expression.Variable(enumeratorType, "enumerator");
var getEnumeratorCall = Expression.Call(collection, enumerableType.GetMethod("GetEnumerator"));
var enumeratorAssign = Expression.Assign(enumeratorVar, getEnumeratorCall);
// The MoveNext method's actually on IEnumerator, not IEnumerator<T>
var moveNextCall = Expression.Call(enumeratorVar, typeof(IEnumerator).GetMethod("MoveNext"));
var breakLabel = Expression.Label("LoopBreak");
var loop = Expression.Block(new[] { enumeratorVar },
enumeratorAssign,
Expression.Loop(
Expression.IfThenElse(
Expression.Equal(moveNextCall, Expression.Constant(true)),
Expression.Block(new[] { loopVar },
Expression.Assign(loopVar, Expression.Property(enumeratorVar, "Current")),
loopContent
),
Expression.Break(breakLabel)
),
breakLabel)
);
return loop;
}
要使用它,您需要提供一个要迭代的集合、一个要替换到循环体中的表达式,以及一个由循环体表达式使用的 ParameterExpression,它将在每次循环迭代时分配给循环变量。
我认为有时候例子胜于雄辩......
var collection = Expression.Parameter(typeof(List<string>), "collection");
var loopVar = Expression.Parameter(typeof(string), "loopVar");
var loopBody = Expression.Call(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }), loopVar);
var loop = ForEach(collection, loopVar, loopBody);
var compiled = Expression.Lambda<Action<List<string>>>(loop, collection).Compile();
compiled(new List<string>() { "a", "b", "c" });
编辑:正如杰罗姆·莫斯特在评论中正确指出的那样,这并不quite镜像 foreach 循环的“真实”行为:这将确保它处理枚举器。 (它还会为每次迭代创建循环变量的新实例,但这对于表达式没有意义)。如果您有足够的动力,那么实现这一点只需转动手柄即可!
对于在家观看的人,我有一个类似的方法来生成“for”循环:
public static Expression For(ParameterExpression loopVar, Expression initValue, Expression condition, Expression increment, Expression loopContent)
{
var initAssign = Expression.Assign(loopVar, initValue);
var breakLabel = Expression.Label("LoopBreak");
var loop = Expression.Block(new[] { loopVar },
initAssign,
Expression.Loop(
Expression.IfThenElse(
condition,
Expression.Block(
loopContent,
increment
),
Expression.Break(breakLabel)
),
breakLabel)
);
return loop;
}
这相当于以下语句,其中伪变量与上述方法中的表达式匹配:
for (loopVar = initValue; condition; increment)
{
loopContent
}
同样,loopContent、condition 和increment 是使用loopVar 的表达式,并且loopVar 在每次迭代时分配。