使用表达式树转储对象





5.00/5 (4投票s)
使用表达式树转储对象。
不,我并非建议去除对象。
我的一个同事被问到,我是否知道一种方法,可以将未知类型对象的列表转储到DataTable中,其性能比他正在使用的方法更好。
被转储的对象通常具有十几个属性,但为了这篇文章的方便,让我们假设它们看起来像这样:
class SomeClass
{
public int Property1 { get; set; }
public long Property2 { get; set; }
public string Property3 { get; set; }
public object Property4 { get; set; }
public DateTimeOffset Property5 { get; set; }
}
他正在使用的代码如下所示:
var properties = objectType.GetProperties();
foreach (object obj in objects)
{
foreach (var property in properties)
{
property.GetValue(obj, null);
}
}
对于包含一百万个对象的列表,在我的机器上这大约需要 6000 毫秒。
我立即想到了表达式树!
如果在编译时知道对象的类型,它将类似于这样:
Expression<Func<SomeClass, int>> expression = o => o.Property1;
var compiled = expression.Compile();
var propertyValue = compiled.Invoke(obj);
但是,在编译时,对象的类型以及其属性的类型是未知的。因此,对于每个属性,我们将需要一个这样的表达式树:
Expression<Func<object, object>> expression = o => ((SomeClass)o).Property1;
前面的表达式获取将类型为object的参数转换为对象类型的属性的值。结果也必须转换为类型object
,因为结果的类型必须与表达式的返回值类型匹配。
对于相同类型的对象,属性访问器的集合将按这种方式构建:
var compiledExpressions = (from property in properties
let objectParameter = Expression.Parameter(typeof(object), "o")
select
Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Property(
Expression.Convert(
objectParameter,
objectType
),
property
),
typeof(object)
),
objectParameter
).Compile()).ToArray();
看起来有点复杂,但是使用这段代码读取同一对象集中所有对象的全部属性:
foreach (object obj in objects)
{
foreach (var compiledExpression in compiledExpressions)
{
compiledExpression(obj);
}
}
在我的机器上大约需要 150 毫秒。
没错,是之前时间的 2.5%。