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

Мне нужна улучшенная производительность Activator.CreateInstance() и встретил эту статью от Мирона Абрамсона, который использует factory для создания экземпляра в ИЛ, а затем кешировать его. (Я включил код ниже с сайта Мирона Абрамсона, если он каким-то образом исчезнет). Я новичок в коде IL Emit и ничего кроме Activator.CreateInstance() для создания экземпляра класса, и любая помощь будет очень благодарна.

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

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


// 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 от комментатора на своем сообщении 2010-01-11

public static class FastObjectFactory2<T> where T : class, new()
{
    public static Func<T> 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<T>)
        dynMethod.CreateDelegate(typeof(Func<T>));
    }
}

Ответ 1

Я рассказываю об этом как о лучшем показе лучшего объекта создания factory, используя текущие ответы (2010-01-11), согласно моим тестам. Я заметил, что использование кеша работает лучше всего, когда итерации находятся где-то ниже 99,999. Если вы собираетесь загружать более 99,999, лучше не использовать кеш. Поскольку это может быть так, я создал что-то, что позволит вам использовать кеш или нет. Мой текущий проект иногда загружает миллионы записей, а в других случаях загружает только один. В любом случае, я помещаю это там, чтобы понять, что вы думаете. Обратите внимание, что приведенный ниже код для ctor, который имеет 1 arg, нужно было бы создать аналогичный factory для более чем 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&ltComment, ObjectId&gt
            .CreateObject.Invoke(new ObjectId());

        // Method 2 (with cache)
        Comment comment2 = ObjectFactoryFactory&ltComment, ObjectId&gt
            .CreateObjectWithCache.Invoke(new ObjectId());

        // Method 3 (without helper factory ObjectFactoryFactory)
        Comment comment3 = ObjectFactory&ltComment, ObjectId&gt
            .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&ltT, P1&gt where T : class
{
    static Hashtable cache = Hashtable.Synchronized(new Hashtable());

    public static Func&ltP1, T&gt CreateObject
    {
        get { return ObjectFactory&ltT, P1&gt.CreateObject; }
    }

    public static Func&ltP1, T&gt CreateObjectWithCache
    {
        get
        {
            return ObjectFactory&ltT, P1&gt.UseCache(cache);
        }
    }
}

// Main object creation factory class.
public static class ObjectFactory&ltT, P1&gt where T : class
{

    static Func&ltP1, T&gt _createObject;

    public static Func&ltP1, T&gt CreateObject
    {
        get
        {
            if (_createObject != null) return _createObject;
            _createObject = CreateDelegate();
            return _createObject;
        }
    }

    static Func&ltP1, T&gt 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&ltP1, T&gt)dynMethod.CreateDelegate(typeof(Func&ltP1, T&gt));
    }

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

        } 
        return c; 
    }
}

Ответ 2

Я делал немного тестирования с этим и в качестве продолжения оригинальной статьи Мирона (здесь), я что активатор .NET 4.0 намного быстрее, чем раньше. Некоторые результаты от версии его приложения изменились, чтобы показать тайминги в миллисекундах:

.NET 3.5 build

Number of iterates: 1000000
Activator.CreateInstance(Type):                           4150
Activator.CreateInstance<T>():                            1288
FastObjectFactory.CreateObjec (empty cache):                33
FastObjectFactory.CreateObjec (cache full):                 28
ItemFactory.GetNewItem:                                   1283


.NET 4.0 build

Number of iterates: 1000000
Activator.CreateInstance(Type):                            138
Activator.CreateInstance<T>():                             151
FastObjectFactory.CreateObjec (empty cache):                28
FastObjectFactory.CreateObjec (cache full):                 22
ItemFactory.GetNewItem:                                    156

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

Одна из проблем, с которой я столкнулся с оригинальным решением, опубликованным здесь, заключается в том, что я не обязательно знаю тип объектов, которые я хочу во время компиляции, - я имею только ссылку на тип. Теперь (если я не являюсь duffer), это означает, что я не могу использовать общее решение здесь или просто вариацию на нем.

Итак, это версия, с которой я столкнулся, что решает проблему. Он также обнаружил небольшую медлительность в .NET 4.0 Activator при использовании параметров конструктора:

// For use with no-parameter constructors. Also contains constants and utility methods
public static class FastActivator
{
    // THIS VERSION NOT THREAD SAFE YET
    static Dictionary<Type, Func<object>> constructorCache = new Dictionary<Type, Func<object>>();

    private const string DynamicMethodPrefix = "DM$_FastActivator_";

    public static object CreateInstance(Type objType)
    {
        return GetConstructor(objType)();
    }

    public static Func<object> GetConstructor(Type objType)
    {
        Func<object> constructor;
        if (!constructorCache.TryGetValue(objType, out constructor))
        {
            constructor = (Func<object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<object>), new Type[] { });
            constructorCache.Add(objType, constructor);
        }
        return constructor;
    }

    public static object BuildConstructorDelegate(Type objType, Type delegateType, Type[] argTypes)
    {
        var dynMethod = new DynamicMethod(DynamicMethodPrefix + objType.Name + "$" + argTypes.Length.ToString(), objType, argTypes, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        for (int argIdx = 0; argIdx < argTypes.Length; argIdx++)
        {
            ilGen.Emit(OpCodes.Ldarg, argIdx);
        }
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(argTypes));
        ilGen.Emit(OpCodes.Ret);
        return dynMethod.CreateDelegate(delegateType);
    }
}

// For use with one-parameter constructors, argument type = T1
public static class FastActivator<T1>
{
    // THIS VERSION NOT THREAD SAFE YET
    static Dictionary<Type, Func<T1, object>> constructorCache = new Dictionary<Type, Func<T1, object>>();
    public static object CreateInstance(Type objType, T1 arg1)
    {
        return GetConstructor(objType, new Type[] { typeof(T1) })(arg1);
    }
    public static Func<T1, object> GetConstructor(Type objType, Type[] argTypes)
    {
        Func<T1, object> constructor;
        if (!constructorCache.TryGetValue(objType, out constructor))
        {
            constructor = (Func<T1, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, object>), argTypes);
            constructorCache.Add(objType, constructor);
        }
        return constructor;
    }
}

// For use with two-parameter constructors, argument types = T1, T2
public static class FastActivator<T1, T2>
{
    // THIS VERSION NOT THREAD SAFE YET
    static Dictionary<Type, Func<T1, T2, object>> constructorCache = new Dictionary<Type, Func<T1, T2, object>>();
    public static object CreateInstance(Type objType, T1 arg1, T2 arg2)
    {
        return GetConstructor(objType, new Type[] { typeof(T1), typeof(T2) })(arg1, arg2);
    }

    public static Func<T1, T2, object> GetConstructor(Type objType, Type[] argTypes)
    {
        Func<T1, T2, object> constructor;
        if (!constructorCache.TryGetValue(objType, out constructor))
        {
            constructor = (Func<T1, T2, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, T2, object>), argTypes);
            constructorCache.Add(objType, constructor);
        }
        return constructor;
    }
}

// For use with three-parameter constructors, argument types = T1, T2, T3
// NB: could possibly merge these FastActivator<T1,...> classes and avoid generic type parameters
// but would need to take care that cache entries were keyed to distinguish constructors having 
// the same number of parameters but of different types. Keep separate for now.
public static class FastActivator<T1, T2, T3>
{
    // THIS VERSION NOT THREAD SAFE YET
    static Dictionary<Type, Func<T1, T2, T3, object>> constructorCache = new Dictionary<Type, Func<T1, T2, T3, object>>();
    public static object CreateInstance(Type objType, T1 arg1, T2 arg2, T3 arg3)
    {
        return GetConstructor(objType, new Type[] { typeof(T1), typeof(T2), typeof(T3) })(arg1, arg2, arg3);
    }

    public static Func<T1, T2, T3, object> GetConstructor(Type objType, Type[] argTypes)
    {
        Func<T1, T2, T3, object> constructor;
        if (!constructorCache.TryGetValue(objType, out constructor))
        {
            constructor = (Func<T1, T2, T3, object>)FastActivator.BuildConstructorDelegate(objType, typeof(Func<T1, T2, T3, object>), argTypes);
            constructorCache.Add(objType, constructor);
        }
        return constructor;
    }
}

Ниже приведены некоторые результаты работы. Обратите внимание, что это для создания 1 миллиона объектов и таймингов в миллисекундах снова:

Activator.CreateInstance(objType) - parameterless constructor: 153
FastActivator.CreateInstance(objType) - parameterless constructor: 86
Using FastActivator.GetConstructor and calling it repeatedly - parameterless constructor: 34
Activator.CreateInstance(objType) with 1 constructor arg: 3183
FastActivator.CreateInstance(objType) with 1 constructor arg: 257
FastActivator.GetConstructor and calling it repeatedly with 1 constructor arg: 126
Activator.CreateInstance(objType) with 3 constructor args: 4403
FastActivator.CreateInstance(objType) with 3 constructor args: 640
FastActivator.GetConstructor and calling it repeatedly with 3 constructor args : 405
FastActivator.GetConstructor and calling it repeatedly with 3 constructor args; args created only once : 19

Ответ 3

Я написал это некоторое время назад. Он использует деревья выражений .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();
    }
}

Имейте в виду это!: →

Ответ 4

Обычно я использую следующий метод, чтобы полностью избежать Activator.CreateInstance() в родовых классах.

Мне нужно, чтобы делегат Func<T> передавался ему конструктором. Класс вызовет это, если ему нужно создать экземпляр нового экземпляра T. Это сделает экземпляр таким же быстрым, как и в не-универсальном классе, и является более простым и элегантным IMO, чем сложный класс, который испускает IL. Другой потенциал роста (VS New T()) при использовании этого метода заключается в том, что вы можете создать экземпляр класса с параметризованным конструктором.

Изменить: я обновил код с помощью параметризованного примера конструктора для запроса BillW.

class GenericClass<T>
{
    public GenericClass(Func<T> classFactory)
    {
        this.ClassFactory = classFactory;
    }
    Func<T> ClassFactory;

    public void CreateNewInstDemo()
    {
        //T NewObject = New T(); // <- calls the slow Activator.CreateInstance() in IL
        T NewObject = ClassFactory(); // Calls the quick IL mnemonic 'newobj'
    }
}
class GenericClassParamsDemo<T>
{
    public GenericClassParamsDemo(Func<int, T> classFactory)
    {
        this.ClassFactory = classFactory;
    }
    Func<int, T> ClassFactory;

    public void CreateNewInstDemo()
    {
        T NewObject = ClassFactory(5); // Calls the quick IL mnemonic 'newobj'
    }
}
class ClassToCreate
{
    public int Number { get; set; }
    public ClassToCreate()
    {
        // Default constructor
    }
    public ClassToCreate(int number)
    {
        // Constructor With Parameter
        this.Number = number;
    }
}
class Program
{
    static void Main(string[] args)
    {
        GenericClass<ClassToCreate> gc =
        new GenericClass<ClassToCreate>(() => { return new ClassToCreate(); });
        // Call method which uses delegate to create a new instance
        gc.CreateNewInstDemo();

        GenericClassParamsDemo<ClassToCreate> gcParams =
        new GenericClassParamsDemo<ClassToCreate>((number) => { return new ClassToCreate(number); });
         // Call method which uses delegate to create a new instance with params
        gcParams.CreateNewInstDemo();
    }
}

Ответ 5

Я понятия не имел, что новый T() был медленным в родовом классе. Моя проблема - это действительно что-то еще - если бы я знал, что Т было во время компиляции, все будет в порядке, но я хочу быстро создать экземпляр класса, указанного в информации о конфигурации (т.е. Строки, содержащие имена узлов сборки/класса) во время выполнения. Я использую кеш объектов Type для загрузки сборки и определения типа в ней только один раз, поэтому последнее препятствие заключается в том, чтобы быстро создавать экземпляры, которые были предметом моего предыдущего сообщения.

В любом случае, исходя из моего вывода о том, что .NET 4.0 ускоряется в таких вещах, которые я тестировал с версией вашего примера, вызывая каждый метод CreateNewInstxxx 1,000,000 раз. Сроки в миллисекундах:

class GenericClass<T> where T : new()
{
    Func<T> ClassFactory;    
    public GenericClass(Func<T> classFactory)    
    {        
        this.ClassFactory = classFactory;    
    }    
    public T CreateNewInstQuick()    
    {        
        return ClassFactory(); // Calls the quick IL mnemonic 'newobj'   
    }
    public T CreateNewInstStd()
    {
        return new T(); // <- calls the slow Activator.CreateInstance() in IL
    }
}

.NET 3.5
CreateNewInstQuick: 35
CreateNewInstStd: 1298

.NET 4.0
CreateNewInstQuick: 29
CreateNewInstStd: 165

Итак,.NET 4.0 намного быстрее, чем раньше. Код, сгенерированный компилятором для метода CreateNewInstStd(), выглядит как в обоих случаях, поэтому кажется, что ускорение заканчивается улучшенным методом Activator.CreateInstance<T>():

public T CreateNewInstStd()
{
    T t1 = default(T);
    if (t1 != null)
    {
        T t2 = default(T);
        return t2;
    }
    return Activator.CreateInstance<T>();
}

Изменить: для добавления этой производительности примерно то же самое, когда GenericClass ограничено ссылочными типами через это:

class GenericClass<T> where T :  class, new()

и в этом случае код, сгенерированный компилятором, будет следующим:

public T CreateNewInstStd()
{
    return Activator.CreateInstance<T>();
}

Ответ 6

Я думаю, что первое, что вам нужно изменить, это делегат 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();
}

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

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

Ответ 7

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

Ответ 8

Чтобы следовать, это ответ @thames, преобразованный в VB.NET.

//' Original source: http://stackoverflow.com/info/2024435/how-to-pass-ctor-args-in-activator-createinstance-or-use-il/2045313#2045313
//' Converted to VB with: http://www.developerfusion.com/tools/convert/csharp-to-vb/
//' code updated 2010-06-01
//' class that creates comment objects
Public Class CreatesSomeObject

    //' method that creates a comment object
    Public Sub CreateComment()

        //' Method 1 (without cache)
        Dim comment1 As Comment = ObjectFactoryFactory(Of Comment, ObjectId).CreateObject.Invoke(New ObjectId())

        //' Method 2 (with cache)
        Dim comment2 As Comment = ObjectFactoryFactory(Of Comment, ObjectId).CreateObjectWithCache.Invoke(New ObjectId())

        //' Method 3 (without helper factory ObjectFactoryFactory)
        Dim comment3 As Comment = ObjectFactory(Of Comment, ObjectId).CreateObject.Invoke(New ObjectId())

    End Sub

End Class

Public Class Comment

    Public Sub New(ByVal objectId As ObjectId)
    End Sub

End Class

Public Class ObjectId
End Class

//' This is optional class. Just helps in creating objects when
//' a cache is needed or not needed.
Public NotInheritable Class ObjectFactoryFactory(Of T As Class, P1)

    Private Sub New()
    End Sub

    Shared cache As Hashtable = Hashtable.Synchronized(New Hashtable())

    Public Shared ReadOnly Property CreateObject() As Func(Of P1, T)
        Get
            Return ObjectFactory(Of T, P1).CreateObject
        End Get
    End Property

    Public Shared ReadOnly Property CreateObjectWithCache() As Func(Of P1, T)
        Get
            Return ObjectFactory(Of T, P1).UseCache(cache)
        End Get
    End Property
End Class

//' Main object creation factory class.
Public NotInheritable Class ObjectFactory(Of T As Class, P1)

    Private Sub New()
    End Sub

    Shared _createObject As Func(Of P1, T)

    Public Shared ReadOnly Property CreateObject() As Func(Of P1, T)
        Get
            If _createObject IsNot Nothing Then
                Return _createObject
            End If
            _createObject = CreateDelegate()
            Return _createObject
        End Get
    End Property

    Private Shared Function CreateDelegate() As Func(Of P1, T)

        Dim objType As Type = GetType(T)
        Dim types As Type() = {GetType(P1)}
        Dim dynMethod = New DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, types, objType)
        Dim ilGen As ILGenerator = 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 DirectCast(dynMethod.CreateDelegate(GetType(Func(Of P1, T))), Func(Of P1, T))

    End Function

    Public Shared Function UseCache(ByVal cache As Hashtable) As Func(Of P1, T)

        Dim t As Type = GetType(T)
        Dim c As Func(Of P1, T) = TryCast(cache(t), Func(Of P1, T))

        If c Is Nothing Then

            SyncLock cache.SyncRoot

                c = TryCast(cache(t), Func(Of P1, T))
                If c IsNot Nothing Then
                    Return c
                End If
                c = CreateDelegate()
                cache.Add(t, c)

            End SyncLock

        End If

        Return c

    End Function

End Class

Обратите внимание, что использование//'для комментариев позволяет корректно работать с колоризатором кода SO. После вставки этого кода в ваш проект замените//'на'.