【发布时间】:2026-01-23 09:40:01
【问题描述】:
我有一个这个简单模型的列表:
// model:
public class Test {
public int ID { get; set; }
public string Name { get; set; }
}
作为var list = new List<Test>() { /* some items here */ };。我正在通过这个 sn-p 从list 生成一个DataTable:
var dataTable = new DataTable();
dataTable.Columns.Add("ID", typeof(int));
dataTable.Columns.Add("Name", typeof(string));
foreach (var item in list) {
var dr = dataTable.NewRow();
dr["ID"] = item.ID;
dr["Name"] = item.Name;
dataTable.Rows.Add(dr);
}
现在我正在尝试生成一些表达式树以在运行时执行上述 sn-p(以通用方式)。然而,我的尝试让我来到了这里:
private static Action<DataTable, IEnumerable<T>> GetAction() {
if (_filler != null)
return;
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var tableParam = Expression.Parameter(typeof(DataTable), "targetTable");
var rowsParam = Expression.Parameter(typeof(IEnumerable<T>), "rows");
var loopVariable = Expression.Parameter(typeof(T), "item");
var columnsVariable = Expression.Parameter(typeof(DataColumnCollection), "columns");
var columnsAssign = Expression.Assign(columnsVariable,
Expression.Property(tableParam, typeof(DataTable).GetProperty("Columns")));
var headerExpressions = new List<Expression>();
var bodyExpressions = new List<Expression>();
var newRowParam = Expression.Parameter(typeof(DataRow), "currentRow");
var newRowAssign = Expression.Assign(newRowParam, Expression.Call(tableParam, typeof(DataTable).GetMethod("NewRow")));
foreach (var prop in props) {
var getMethod = prop.GetGetMethod(false);
if (getMethod == null)
continue;
var attr = prop.GetCustomAttribute<UdtColumnAttribute>();
var name = attr == null ? prop.Name : attr.ColumnName;
var headerNameParam = Expression.Parameter(typeof(string), "columnName");
var headerNameAssign = Expression.Assign(headerNameParam, Expression.Constant(name, typeof(string)));
var headerTypeParam = Expression.Parameter(typeof(Type), "columnType");
var headerTypeAssign = Expression.Assign(headerTypeParam, Expression.Constant(prop.PropertyType, typeof(Type)));
var columnsAddMethod = Expression.Call(columnsVariable,
typeof(DataColumnCollection).GetMethod("Add", new[] { typeof(string), typeof(Type) }),
headerNameParam, headerTypeParam);
headerExpressions.AddRange(new Expression[] {
headerNameParam,
headerNameAssign,
headerTypeParam,
headerTypeAssign,
columnsAddMethod,
});
var indexerProp = typeof(DataRow).GetProperty("Item", new[] { typeof(string) });
var indexerParam = Expression.Property(newRowParam, indexerProp, Expression.Constant(name, typeof(string)));
var propertyReaderMethod = Expression.Call(loopVariable, getMethod);
var assign = Expression.Assign(indexerParam, Expression.TypeAs(propertyReaderMethod, typeof(object)));
bodyExpressions.AddRange(new Expression[] { indexerParam, propertyReaderMethod, assign });
}
var finalExpressions = new List<Expression>() {
tableParam,
rowsParam,
loopVariable,
columnsVariable,
columnsAssign,
newRowParam,
newRowAssign,
};
finalExpressions.AddRange(headerExpressions);
var loop = ExpressionHelper.ForEach(rowsParam, loopVariable, Expression.Block(bodyExpressions));
finalExpressions.Add(loop);
var compilable = Expression.Block(finalExpressions);
var code = compilable.ToString();
Trace.WriteLine(code);
var compiled = Expression.Lambda<Action<DataTable, IEnumerable<T>>>(compilable, tableParam, rowsParam).Compile();
return compiled;
}
但是,当我调用.Compile() 方法时(在块的末尾,就在return 之前)我得到了这个错误:
“System.InvalidOperationException”类型的异常发生在 System.Core.dll 但未在用户代码中处理
附加信息:类型的变量“item” 'TestEntity' 引用自 范围'',但没有定义
你知道我在这里错过了什么吗?提前致谢。干杯。
更新: 这是循环生成器:
public static class ExpressionHelper {
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;
}
}
【问题讨论】:
-
loopVariable被定义为一个参数,但你永远不会将它提供给你的 lambda。您的意思是使用Expression.Variable吗? -
@Rob 我把
loopVariable改成了Expression.Variable,但是问题还是存在的。
标签: c# c#-4.0 lambda expression-trees