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

tha*_*mes 11 .net parameters performance il createinstance

我需要一个性能增强的Activator.CreateInstance()并且Miron Abramson发现了这篇文章,它使用工厂在IL中创建实例然后缓存它.(我已经在Miron Abramson的网站上包含了以下代码,以防它以某种方式消失).我是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;
    }
}

Run Code Online (Sandbox Code Playgroud)

在评论员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>));
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*ran 6

我一直在做这方面的测试,作为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
Run Code Online (Sandbox Code Playgroud)

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

我在这里发布的原始解决方案遇到的一个问题是,我不一定知道编译时我想要的对象类型 - 我只有一个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;
    }
}
Run Code Online (Sandbox Code Playgroud)

一些性能结果如下.请注意,这是为了再创建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
Run Code Online (Sandbox Code Playgroud)


tha*_*mes 4

根据我的测试,我将其作为迄今为止使用当前(2010-01-11)答案的最佳性能对象创建工厂。我确实注意到,当迭代次数低于 99,999 时,使用缓存效果最佳。如果要加载超过 99,999 个,最好不要使用缓存。因为可能是这种情况,所以我创建了一些允许您使用缓存或不使用缓存的东西。我当前的项目有时会加载数百万条记录,有时只加载一条记录。不管怎样,我把这个放在那里看看你的想法是什么。请注意,下面的代码适用于具有 1 个参数的 ctor,因此必须为超过 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<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; 
    }
}
Run Code Online (Sandbox Code Playgroud)