Как передать аргументы ctor в Activator.CreateInstance или использовать IL? - PullRequest
11 голосов
/ 08 января 2010

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

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

Если возможно, я бы хотел использовать метод, аналогичный CreateObjectFactory<T>(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;
    }
}

Обновление кода Мирона от комментатора на его пост 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>));
    }
}

Ответы [ 8 ]

6 голосов
/ 01 июля 2011

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

.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

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

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

Так что это версия, которую я собрал вместе, которая решает проблему. Это также обнаружило небольшую медлительность в .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
4 голосов
/ 12 января 2010

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

Я понятия не имел, что новый T () был медленным в универсальном классе. Моя проблема, правда, в другом: если бы я знал, что такое 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>();
}
1 голос
/ 01 июля 2011

Обычно я использую следующий метод, чтобы полностью избежать 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();
    }
}
1 голос
/ 08 января 2010

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

Веселись! : ->

0 голосов
/ 03 ноября 2010

Следуя, ответ @thames преобразуется в VB.NET.

//' Original source: /1628494/kak-peredat-argumenty-ctor-v-activator-createinstance-ili-ispolzovat-il#1628508
//' 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 color colorizer работать правильно. После вставки этого кода в ваш проект замените // 'на'.

0 голосов
/ 10 января 2010

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

0 голосов
/ 08 января 2010

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

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

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

...