编译Lambda和Activator.CreateInstance()

时间:2015-10-07 23:48:00

标签: lambda delegates expression-trees concurrentdictionary createinstance

我试图减少在以下循环的每次迭代中调用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)
{
}

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

1 个答案:

答案 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();