1

I'm trying to cut back on the performance costs of calling Activator.CreateInstance() on each iteration of the following loop (simplified):

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

Based on what I've read the best way to go about this would be to compile a delegate and cache it. This would slow things down on the first iteration (while the delegate is being built) but would greatly improve performance on subsequent iterations. This acceptable since I'm iterating an upwards of a 1000 times. To further complicate matters I'm executing this loop in parallel so whatever caching mechanism will have to be thread safe (ConcurentDictionary). Making the method the loop is in into a generic isn't possible since the type I'm passing into Activator.CreateInstance() is determined by a choice made by the user through a GUI and passed to my function. For some reference here is the method signature:

public static void InsertByTable(IEnumerable<DataTable> chunkedTable, Type type)

So I want to do something sort of like this (this is pseudo code):

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)
{
}

But I'm kind of at a loss how to actually build the expression or even if this is the right approach.

4

1 に答える 1

1

この場合、表現を構築することが道だと思います。通過するすべての行で、リフレクションを使用してオブジェクトを構築しています。それは安くはありません。オブジェクトを初期化するための式を構築した場合、式を構築するための費用を一度支払うだけで、パフォーマンスが大幅に向上するラムダを残します。

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 に答える