使用表达式树为泛型类分配属性
本文关键字:泛型类 分配 属性 表达式 | 更新日期: 2023-09-27 18:04:31
救命啊,我好像有点迷路了。
昨天问了这个问题之后,我决定研究一下表达式树。我找到了一个不错的小地方开始,这是我到目前为止的内容:
// Gets the property type
ParameterExpression paramProperty = Expression.Parameter(property.PropertyType);
// Gets the value from row[0] (SqlDataReader)
ParameterExpression paramValue = Expression.Parameter(row[0].GetType());
// really no clue, makes a property so to speak?
MemberExpression prop = Expression.Property(paramProperty, property);
// assigns the property the value from the SqlDataReader
BinaryExpression assign = Expression.Assign(prop, paramValue);
// adds to an expression list ready for compilation
exps.Add(assign);
// allows things to be executed sequentially?
BlockExpression blockExpression = exps.Count > 0 ? Expression.Block(exps) : Expression.Block(Expression.Empty());
// create the parameter array
List<ParameterExpression> paramArr = new List<ParameterExpression>();
paramArr.Add(paramProperty);
paramArr.Add(paramValue);
// get a lambda so I can compile this for re-use
Expression<Action<T>> lamExp = Expression.Lambda<Action<T>>(blockExpression, paramArr);
首先,我的评论是否正确?我将这些信息以一种良好的老式教程和msdn文档的方式拼凑起来。
从我一直在阅读的比特中,我认为我应该能够编译这个,将它存储在一个字典中,并以类型作为键,并在需要时调用它。如:
ConcurrentDictionary<Type, ??> ExpressionCache;
if(ExpressionCache.ContainsKey(typeof(T))
{
// property is the variable of a foreach loop of type PropertyInfo
ExpressionCache[typeof(T)](property);
} // else do the first piece of code...
总的来说,
- 我的方向对吗?
- 我对表达式的评论是否正确? 我应该使用什么类型的并发字典?
- 当我缓存引用时,我如何将不同的参数传递给lambda?
只要解释清楚,欢迎任何改进或建议。我试图了解它是如何工作的,而不仅仅是使它工作:)
听起来你更想要评论,对吧?
首先,如何在编译时使用SqlReader ?
// Gets the value from row[0] (SqlDataReader)
ParameterExpression paramValue = Expression.Parameter(Type.GetTypeFromHandle(row[0].GetType()));
你的lambda有两个参数…
我认为你真正想要的是一个Func<SqlDataReader, T>
。这样,你给它SqlDataReader它就会生成一个t,所以:
var list = new List<T>();
// get a SqlDataReader
while (reader.Read())
{
Func<SqlDataReader, T> readRow = GetReader<T>();
list.Add(readRow(reader);
}
我将把你的缓存存储为
ConcurrentDictionary<Type, Delegate> ExpressionCache; // can't use T here since each Func will have a different T
然后在检索时将其强制转换为调用者的适当委托类型(让您的方法接受泛型参数:
) public Func<SqlDataReader, T> GetReader<T>()
{
Delegate d;
if(!ExpressionCache.TryGetValue(typeof(T), out d)
{
ExpressionCache[typeof(T)] = d = // build and compile lambda
}
// cast to strong typed delegate...we don't want to have to DynamicInvoke...that's slow
return (Func<SqlDataReader, T>)d;
}
所以…至于你的lambda生成器/编译器:
// hang on to row[string] property
var indexerProperty = typeof(SqlDataReader).GetProperty("Item", new[] { typeof(string) });
// list of statements in our dynamic method
var statements = new List<Expression>();
// store instance for setting of properties
ParameterExpression instanceParameter = Expression.Variable(typeof(T));
ParameterExpression sqlDataReaderParameter = Expression.Parameter(typeof(SqlDataReader));
// create and assign new T to variable: var instance = new T();
BinaryExpression createInstance = Expression.Assign(instanceParameter, Expression.New(typeof(T)));
statements.Add(createInstance);
foreach (var property in typeof(T).GetProperties())
{
// instance.MyProperty
MemberExpression getProperty = Expression.Property(instanceParameter, property);
// row[property] -- NOTE: this assumes column names are the same as PropertyInfo names on T
IndexExpression readValue = Expression.MakeIndex(sqlDataReaderParameter, indexerProperty, new[] { Expression.Constant(property.Name) });
// instance.MyProperty = row[property]
BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(readValue, property.PropertyType));
statements.Add(assignProperty);
}
var returnStatement = instanceParameter;
statements.Add(returnStatement);
var body = Expression.Block(instanceParameter.Type, new[] { instanceParameter }, statements.ToArray());
/* so we end up with
* T Read(SqlDataReader row)
* {
* var x = new T();
* x.Prop1 = (cast)row["Prop1"]
* x.Prop2 = (cast)row["Prop2"]
* x.Prop3 = (cast)row["Prop3"]
* x.Prop4 = (cast)row["Prop4"]
* etc.
* return x
* }
*/
var lambda = Expression.Lambda<Func<SqlDataReader, T>>(body, sqlDataReaderParameter);
// cache me!
return lambda.Compile();
还没有测试过,所以请小心使用,自己试一试。
我不确定我是否正确理解了你的意图用法…是吗?