1

我试图减少在以下循环的每次迭代中调用 Activator.CreateInstance() 的性能成本(简化):

foreach (DataRow dr in chunk.Rows)
{
   var objectToInsert = Activator.CreateInstance(type);
}

根据我读过的最好的方法是编译一个委托并缓存它。这会在第一次迭代时减慢速度(在构建委托时),但会大大提高后续迭代的性能。这是可以接受的,因为我迭代了 1000 次以上。为了使事情更加复杂,我正在并行执行这个循环,所以任何缓存机制都必须是线程安全的(ConcurentDictionary)。使循环所在的方法成为泛型是不可能的,因为我传递给 Activator.CreateInstance() 的类型是由用户通过 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)
{
}

但我有点不知所措如何实际构建表达式,或者即使这是正确的方法。

4

1 回答 1

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();
于 2015-10-08T00:44:43.103 回答