问题 如何在Activator.CreateInstance中传递ctor args或使用IL?


我需要一个性能增强的Activator.CreateInstance()并且遇到了 本文 由Miron Abramson使用工厂在IL中创建实例然后缓存它。 (我已经在Miron Abramson的网站上面包含了以下代码,以防它以某种方式消失)。我是IL Emit代码的新手,除了Activator.CreateInstance()之外的任何东西,用于实例化一个类,任何帮助都会非常感激。

我的问题是我需要创建一个带有参数的ctor对象的实例。我看到有一种传递参数类型的方法,但有没有办法传入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的帖子中更新Miron的代码

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

8794
2018-01-07 23:20


起源

我发现(预期)迭代次数极大地决定了使用哪个Activator。例如,当少于99,999次迭代时,缓存似乎运行良好。如果不止于此,则非缓存实现会更快。 - thames


答案:


我一直在做这方面的测试,并作为Miron原创文章的后续内容(这里),我发现.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

然而,这是一个无参数的构造函数,我也注意到当使用带参数的构造函数时,激活器仍然有点慢,如下所示。

我在这里发布的原始解决方案遇到的一个问题是我不一定知道编译时我想要的对象类型 - 我只有一个Type引用。现在(除非我是一个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;
    }
}

一些表现结果如下。请注意,这是为了再次创建100万个对象和时间(以毫秒为单位):

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

6
2017-07-01 14:06



感谢您的辛勤工作,并在此主题中进行测试!有趣的是,在.NET 4.0中,System.RunTime'有一个带签名的方法:'CreateInstanceSlow(bool publicOnly,bool skipCheckThis,bool fillCache)'和'Activator'对'CreateInstance'方法有25个重载。 - BillW
每当我看到类似的东西 Func<object.. 我很难过.. - nawfal
@nawfal为什么?这种语言的功能有什么问题?或者它是顶级“对象”类型? - Millie Smith
@MillieSmith是后者。仿制药的忠实粉丝。我知道有些时候它是不可避免的。 - nawfal
我已经调整了一份这个 FastActivator 要使用的版本 ConcurrentDictionary<TKey,TValue> 用它的方法 GetOrAdd(TKey Key, Func<TKey,TValue>)......尚未测试其性能,但我认为它在性能方面会更接近于此 FastActivator 而不是 Activator。这会产生对.Net 4+的依赖。 - Emyr


根据我的测试,到目前为止,我正在使用当前(2010-01-11)的答案将其作为迄今为止最佳的高性能对象创建工厂。我注意到当迭代次数低于99,999时,使用缓存效果最佳。如果要加载超过99,999,最好不要使用缓存。因为可能是这种情况我创建了一些允许你使用缓存的东西。我当前的项目有时会加载数百万条记录,有时只加载一条。无论如何,我把它放在那里,看看你的想法是什么。请注意,下面的代码适用于具有1个arg的ctor,其中一个必须为超过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>
            .CreateObject.Invoke(new ObjectId());

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

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

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

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

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

    static Func&ltP1, T> _createObject;

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

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

    public static Func&ltP1, T> UseCache(Hashtable cache) 
    { 
        Type t = typeof(T);
        Func&ltP1, T> 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; 
    }
}

4
2018-01-11 22:07



我暂时没有听到任何评论,所以我认为这是迄今为止最好的答案。 - thames
优秀。正是我在寻找异常而不是VB :-)。您在UseCache()中缺少2个半冒号。如果添加了Comment和ObjectId类,它还可以使示例更加完整。 - Tim Murphy
非常感谢您在这个帖子中的辛勤工作和测试!看看RepDbg的策略与其他方法的对比情况会很有趣。 - BillW


我刚才写过这篇文章。它使用.NET 3.5 Linq Expression树而不是发射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();
    }
}

有它的乐趣! - >


1
2018-01-08 16:56



谢谢!我试试看。我从未想过linq表达树。 - thames
我更新了Instantiator,因为我对它进行了重构和简化。我注意到我可以不使用该字典,而是利用编译器的泛型机制来保存对已编译的lambda表达式的引用。我们都在过去犯错误,我们会随着时间学习。 ;-) ...但是,表达式树仍然需要一段时间才能编译,但是一旦初始化,它就会尽可能接近本机代码。 - herzmeister
我会看一看。感谢更新! - thames
看起来更新确实在我的测试中加快了速度。 FROM:Instantiator <MyObject> .New(string,int):00:00:00.0249225 TO:Instantiator <MyObject> .New(string,int):00:00:00.0075623 - thames
这是最好的答案。你为什么不使用通用模式? InstantiatorImpl 案件也是?为什么要单独编译树呢? - nawfal


我不知道新的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
2017-07-04 12:24



我想CreateNewInstStd就是这样 return default(T) != null ? default(T) : Activator.CreateInstance<T>() :) - nawfal


我通常使用以下方法来完全避免 Activator.CreateInstance() 在泛型类中。

我需要一个 Func<T> 委托传递给它的构造函数。如果需要实例化T的新实例,该类将调用它。这将使实例化与非泛型类一样快,并且比发出IL的复杂类更简单和优雅的IMO。另一个好处(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
2017-07-01 18:00



有意思,我赞成这个回应。如果您要展示一个如何使用此策略创建带参数的新类的示例,我将不胜感激。 - BillW


如果您不使用简单方法,您的探查器是否向您显示在Activator.CreateInstance()中花费了大量时间(而不是由它调用的任何构造函数)。如果不是这种情况,只需使用Activator.CreateInstance即可。 (似乎没有带有ctor参数的通用CreateInstance()方法,但有一个非泛型的)。


0
2018-01-10 02:03





接下来是@thames的答案转换为VB.NET。

//' Original source: http://stackoverflow.com/questions/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代码着色器正常工作。将此代码粘贴到项目中后替换//'with'。


0
2017-11-03 20:22





我认为你需要改变的第一件事是 CreateObject 代表。我会这样做:

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

然后,这是一个生成a的好方法 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
2018-01-08 20:39



我会试一试!谢谢! - thames
我可能需要有关启动CreateConstructor的最佳方法的更多信息,但这种方法似乎花费的时间最长。从我的测试(没有缓存)相同对象的999个实例需要2秒多一点。这比Activator.CreateObject需要更长的时间。 - thames
Activator.CreateInstance(Type)(string,int):00:00:00.0139442 <br/> Instantiator <MyObject> .New(string,int):00:00:00.0249225 FastObjectFactory.CreateObjec(空缓存):00:00: 00.0023258 FastObjectFactory.CreateObjec(cache full):00:00:00.0000439 LateBoundMethodFactory.CreateConstructor:00:00:02.8543006 - thames
这与Instantiator基本相同,但没有缓存,这就是它不能很好地运行的原因。缓存已编译的表达式至关重要。这就是999个实例仍然不足以超越Activator的原因。 : - /只有在10000之后你才会看到效果。因此,如果应用程序存在很长时间,那么值得考虑。 - herzmeister
很高兴知道,有时会有超过10,000。我会做一些大于10k的测试。 - thames