Как передать ctor args в Активаторе. CreateInstance или использование IL?

Мне нужен улучшенный Активатор производительности. CreateInstance () и столкнулся с этой статьей Miron Abramson, который использует фабрику, чтобы создать экземпляр в IL и затем кэшировать его. (Я включал код ниже от сайта Miron Abramson в случае, если он так или иначе исчезает). Я плохо знаком с IL, Испускают код и что-либо вне Активатора. CreateInstance () для инстанцирования класса и любой справки был бы очень благодарен.

Моя проблема состоит в том, что я должен создать экземпляр объекта, который берет ctor с параметром. Я вижу, что существует способ передать в Типе параметра, но там способ передать в значении ctor параметра также?

Если возможно, я хотел бы использовать метод, подобный CreateObjectFactory(params object[] constructorParams) поскольку некоторые объекты, которых я хочу инстанцировать, могут иметь больше чем 1 ctor параметрический усилитель.


// Source: http://mironabramson.com/blog/post/2008/08/Fast-version-of-the-ActivatorCreateInstance-method-using-IL.aspx
public static class FastObjectFactory
{
    private static readonly Hashtable creatorCache = Hashtable.Synchronized(new Hashtable());
    private readonly static Type coType = typeof(CreateObject);
    public delegate object CreateObject();

    /// 
    /// Create an object that will used as a 'factory' to the specified type T 
   /// 
    public static CreateObject CreateObjectFactory() where T : class
    {
        Type t = typeof(T);
        FastObjectFactory.CreateObject c = creatorCache[t] as FastObjectFactory.CreateObject;
        if (c == null)
        {
            lock (creatorCache.SyncRoot)
            {
                c = creatorCache[t] as FastObjectFactory.CreateObject;
                if (c != null)
                {
                    return c;
                }
                DynamicMethod dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + t.Name, typeof(object), null, t);
                ILGenerator ilGen = dynMethod.GetILGenerator();

                ilGen.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
                ilGen.Emit(OpCodes.Ret);
                c = (CreateObject)dynMethod.CreateDelegate(coType);
                creatorCache.Add(t, c);
            }
        }
        return c;
    }
}

Обновление кода Miron от комментатора на его сообщении 11.01.2010

public static class FastObjectFactory2 where T : class, new()
{
    public static Func CreateObject { get; private set; }

    static FastObjectFactory2()
    {
        Type objType = typeof(T);
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        CreateObject = (Func)
        dynMethod.CreateDelegate(typeof(Func));
    }
}

11
задан thames 21 July 2010 в 20:52
поделиться

4 ответа

Я кладу это как Согласно моим тестам до сих пор лучшая фабрика создания объектов, использующих нынешние ответы (2010-01-11). Я заметил, что использование кеша работает лучше всего, когда итерации где-то находится ниже 99 999. Если вы собираетесь загрузить более 99 999, лучше не использовать кэш. Поскольку это может быть так, я создал что-то, что позволит вам использовать кэш или нет. Мой нынешний проект иногда будет загружать миллионы записей и в другие времена загружать только один. В любом случае, я помещаю это туда, чтобы посмотреть, какие ваши мысли. Обратите внимание, что код ниже предназначен для CTOR, которые имеют 1 ARG, нужно было бы создать аналогичный завод для более чем 1 ARG CTOR.


// code updated 2010-06-01
// class that creates comment objects
public class CreatesSomeObject
{
    // method that creates a comment object
    public void CreateComment()
    {

        // Method 1 (without cache)
        Comment comment1 = ObjectFactoryFactory<Comment, ObjectId>
            .CreateObject.Invoke(new ObjectId());

        // Method 2 (with cache)
        Comment comment2 = ObjectFactoryFactory<Comment, ObjectId>
            .CreateObjectWithCache.Invoke(new ObjectId());

        // Method 3 (without helper factory ObjectFactoryFactory)
        Comment comment3 = ObjectFactory<Comment, ObjectId>
            .CreateObject.Invoke(new ObjectId());
    }
}

// This is optional class. Just helps in creating objects when
// a cache is needed or not needed.
public static class ObjectFactoryFactory<T, P1> where T : class
{
    static Hashtable cache = Hashtable.Synchronized(new Hashtable());

    public static Func<P1, T> CreateObject
    {
        get { return ObjectFactory<T, P1>.CreateObject; }
    }

    public static Func<P1, T> CreateObjectWithCache
    {
        get
        {
            return ObjectFactory<T, P1>.UseCache(cache);
        }
    }
}

// Main object creation factory class.
public static class ObjectFactory<T, P1> where T : class
{

    static Func<P1, T> _createObject;

    public static Func<P1, T> CreateObject
    {
        get
        {
            if (_createObject != null) return _createObject;
            _createObject = CreateDelegate();
            return _createObject;
        }
    }

    static Func<P1, T> CreateDelegate()
    {
        Type objType = typeof(T);
        Type[] types = new[] { typeof(P1) };
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + 
            objType.Name, objType, types, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        // if need more than 1 arg add another Ldarg_x
        // you'll also need to add proper generics and 
        // CreateDelegate signatures
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(types));
        ilGen.Emit(OpCodes.Ret);
        return (Func<P1, T>)dynMethod.CreateDelegate(typeof(Func<P1, T>));
    }

    public static Func<P1, T> UseCache(Hashtable cache) 
    { 
        Type t = typeof(T);
        Func<P1, T> c = cache[t] as Func<P1, T>;
        if (c == null) 
        { 
            lock (cache.SyncRoot) 
            {
                c = cache[t] as Func<P1, T>;
                if (c != null) 
                { 
                    return c; 
                } 
                c = CreateDelegate(); 
                cache.Add(t, c); 
            } 

        } 
        return c; 
    }
}
4
ответ дан 3 December 2019 в 08:55
поделиться
[

]Думаю, первое, что вам нужно изменить, это делегат []CreateObject[]. Я бы сделал так:[

] [
public delegate object CreateObject(params object[] args);
] [

]Затем, вот хороший способ сгенерировать делегата []CreateObject[] из []ConstructorInfo[] (используя деревья выражений):[

] [
/// <summary>
///    Creates and compiles an Expression like this:
/// (object[] args) =>
/// (object)(
///     new {ConstructedType}(args[0], args[1], ...)
/// )
/// </summary>
public static DynamicStaticMethod CreateConstructor(ConstructorInfo constructor)
{
    if (constructor == null) throw new ArgumentNullException("constructor");

    //Create 'args' parameter expression
    ParameterExpression argsParameter = Expression.Parameter(typeof(object[]), "args");

    //Create body expression
    ParameterInfo[] constructorParams = constructor.GetParameters();
    Expression body = Expression.New(
        constructor,
        CreateParameterExpressions(constructorParams, argsParameter)
    );

    //Create and compile lambda
    var lambda = Expression.Lambda<CreateObject>(
        Expression.Convert(body, typeof(object)),
        argsParameter
    );
    return lambda.Compile();
}
] [

]Полная расширенная версия этого компилятора находится в []моем личном репозитории []. Не стесняйтесь просматривать все, что там есть.[

] [

][]Обновление:[] ссылка на репозиторий изменена.[

].
0
ответ дан 3 December 2019 в 08:55
поделиться

Ваш профилировщик показал, что большое количество времени тратится на Activator.CreateInstance () (а не на любой вызываемый им конструктор), если вы не используете простой подход. Если это не так, просто используйте Activator.CreateInstance. (Кажется, что нет универсального метода CreateInstance () с аргументами ctor, но есть неуниверсальный).

0
ответ дан 3 December 2019 в 08:55
поделиться

Я написал это некоторое время назад. Он использует деревья экспрессии .NET 3.5 LINQ, а не излучаем IL, что почти так быстро, и более ремонсировано. Это может занять до 4 аргументов конструктора.

Использование любых конструкторов аргументов, таких как вы хотите сделать, может быть немного медленнее, однако из-за поиска конструктора на основе типов аргументов, но все еще намного быстрее, чем с отражением. Также с эмиссией IL должен быть какой-то поиск, я думаю.

Вы должны указать точный тип, который вы хотите построить, так как нет IOC / DI контейнера. Может быть, вы можете продлить и адаптировать его к вашим потребностям.

// usage:
Cat myCat = Instantiator<Cat>.New("furry", /* isCute*/ true);

using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

static public class Instantiator<TInstance>
{
    static Instantiator()
    {
        Debug.Assert(typeof(TInstance).IsValueType || (typeof(TInstance).IsClass && !typeof(TInstance).IsAbstract),
                String.Concat("The type ", typeof(TInstance).Name, " is not constructable."));
    }

    static public TInstance New()
    {
        return InstantiatorImpl.CtorFunc();
    }

    static public TInstance New<TA>(TA valueA)
    {
        return InstantiatorImpl<TA>.CtorFunc(valueA);
    }

    static public TInstance New<TA, TB>(TA valueA, TB valueB)
    {
        return InstantiatorImpl<TA, TB>.CtorFunc(valueA, valueB);
    }

    static public TInstance New<TA, TB, TC>(TA valueA, TB valueB, TC valueC)
    {
        return InstantiatorImpl<TA, TB, TC>.CtorFunc(valueA, valueB, valueC);
    }

    static public TInstance New<TA, TB, TC, TD>(TA valueA, TB valueB, TC valueC, TD valueD)
    {
        return InstantiatorImpl<TA, TB, TC, TD>.CtorFunc(valueA, valueB, valueC, valueD);
    }

    static private Expression<TDelegate> CreateLambdaExpression<TDelegate>(params Type[] argTypes)
    {
        Debug.Assert(argTypes != null);

        ParameterExpression[] paramExpressions = new ParameterExpression[argTypes.Length];

        for (int i = 0; i < paramExpressions.Length; i++)
        {
            paramExpressions[i] = Expression.Parameter(argTypes[i], String.Concat("arg", i));
        }

        ConstructorInfo ctorInfo = typeof(TInstance).GetConstructor(argTypes);
        if (ctorInfo == null)
        {
            throw new ArgumentException(String.Concat("The type ", typeof(TInstance).Name, " has no constructor with the argument type(s) ", String.Join(", ", argTypes.Select(t => t.Name).ToArray()), "."),
                    "argTypes");
        }

        return Expression.Lambda<TDelegate>(Expression.New(ctorInfo, paramExpressions), paramExpressions);
    }

    static private class InstantiatorImpl
    {
        static public readonly Func<TInstance> CtorFunc = Expression.Lambda<Func<TInstance>>(Expression.New(typeof(TInstance))).Compile();
    }

    static private class InstantiatorImpl<TA>
    {
        static public readonly Func<TA, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TInstance>>(typeof(TA)).Compile();
    }

    static private class InstantiatorImpl<TA, TB>
    {
        static public readonly Func<TA, TB, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TInstance>>(typeof(TA), typeof(TB)).Compile();
    }

    static private class InstantiatorImpl<TA, TB, TC>
    {
        static public readonly Func<TA, TB, TC, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TC, TInstance>>(typeof(TA), typeof(TB), typeof(TC)).Compile();
    }

    static private class InstantiatorImpl<TA, TB, TC, TD>
    {
        static public readonly Func<TA, TB, TC, TD, TInstance> CtorFunc = Instantiator<TInstance>.CreateLambdaExpression<Func<TA, TB, TC, TD, TInstance>>(typeof(TA), typeof(TB), typeof(TC), typeof(TD)).Compile();
    }
}

У Teh Funz с этим! : ->

1
ответ дан 3 December 2019 в 08:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: