我试图减少在以下循环的每次迭代中调用Activator.CreateInstance()的性能成本(简化):
foreach (DataRow dr in chunk.Rows)
{
var objectToInsert = Activator.CreateInstance(type);
}
根据我所阅读的内容,最好的方法是编译委托并缓存它。这会减慢第一次迭代(在构建委托时),但会大大提高后续迭代的性能。这是可以接受的,因为我重复了1000次以上。更复杂的是我要并行执行这个循环,所以无论缓存机制是什么,都必须是线程安全的(ConcurentDictionary)。由于传递给Activator.CreateInstance()的类型I由用户通过GUI做出的选择并传递给我的函数,因此使循环进入循环的方法是不可能的。这里有一些参考方法签名:
public static void InsertByTable(IEnumerable<DataTable> chunkedTable, Type type)
所以我想做一些像这样的事情(这是伪代码):
private static readonly ConcurrentDictionary<Type, Func<object>> CachedConstructors =
new ConcurrentDictionary<Type, Func<object>>();
private static object CreateInstance(Type type)
{
if (type == null)
return;
var constructor = CachedConstructors.GetOrAdd(type, BuildInstance);
constructor(type);
}
private static Func<Type> BuildInstance(Type type)
{
}
但我有点不知道如何实际构建表达式,或者即使这是正确的方法。
答案 0 :(得分:1)
我认为在这种情况下构建表达式是一种方法。对于您经历的每一行,您都使用反射来构建对象。那不会便宜。如果你构建了一个表达式来初始化对象,那么你需要为表达式构建一次,然后使用一个表现更好的lambda。
Func<DataRow, object> CreateInitializer(DataTable table, Type type)
{
var param = Expression.Parameter(typeof(DataRow), "row");
var body = Expression.MemberInit(
Expression.New(type),
from DataColumn c in table.Columns
let prop = type.GetProperty(c.ColumnName)
let value = Expression.Convert(
Expression.Property(param, "Item", Expression.Constant(c.ColumnName)),
prop.PropertyType
)
select Expression.Bind(prop, value)
);
var expr = Expression.Lambda<Func<DataRow, object>>(body, param);
return expr.Compile();
}
然后使用它,只需创建一次并使用它来映射行。
var initializer = CreateInitializer(table, typeof(SomeObject));
var data = table.AsEnumerable().Select(initializer).ToList();