How to pass ctor args in Activator.CreateInstance or use IL?
Asked Answered
A

8

12

I need a performance enhanced Activator.CreateInstance() and came across this article by Miron Abramson that uses a factory to create the instance in IL and then cache it. (I've included code below from Miron Abramson's site in case it somehow disappears). I'm new to IL Emit code and anything beyond Activator.CreateInstance() for instantiating a class and any help would be much appreciative.

My problem is that I need to create an instance of an object that takes a ctor with a parameter. I see there is a way to pass in the Type of the parameter, but is there a way to pass in the value of the ctor parameter as well?

If possible, I would like to use a method similar to CreateObjectFactory<T>(params object[] constructorParams) as some objects I want to instantiate may have more than 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;
    }
}

Update to Miron's code from commentor on his post 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>));
    }
}
Atwell answered 7/1, 2010 at 23:20 Comment(1)
I'm finding (expected) that the number of iterations greatly determines which Activator to use. For instance, a cache seems to work well when less than 99,999 iterations. If more than that then a non cache implementation is faster.Atwell
A
4

I'm putting this up as the so far best performant object creation factory so far using the current (2010-01-11) answers, according to my tests. I did notice that using cache works best when iterations are somewhere below 99,999. If you are going to load more than 99,999 it is best to not use cache. Because this could be the case I've created something that would allow you to use cache or not. My current project will sometimes load millions of records and at other times only load one. Anyways, I'm putting this out there to see what your thoughts are. Note that the code below is for ctor's that have 1 arg, one would have to create a similar factory for more than 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; 
    }
}
Atwell answered 11/1, 2010 at 22:7 Comment(3)
i haven't heard any comments for a while so i'll mark this as the best answer so far.Atwell
Excellent. Exactly what I was looking for exception not in VB :-). You are missing 2 semi colons in UseCache(). It would also make the example more complete if you added Comment and ObjectId classes.Joel
Really appreciate your hard work and testing in this thread ! Would be interesting to see how RepDbg's strategy performed in contrast to the other approaches here.Iver
A
6

I've been doing a bit of testing with this and as a follow-up to Miron's original article (here), I've discovered that the .NET 4.0 Activator is much faster than before. Some results from a version of his app tweaked to show timings in milliseconds:

.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

However this was for a no-parameter constructor and I also noticed that the activator is still a little slow when constructors with parameters are used, as can bse seen below.

One problem I had with the original solution posted here is that I don't necessarily know the type of the objects I want at compile time - I only have a Type reference. Now (unless I'm being a duffer) that means I can't use the generic solution here or a simple variation on it.

So this is a version I've knocked together which addresses the problem. It also showed up the slight slowness in the .NET 4.0 Activator when constructor parameters are used:

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

Some performance results below. Note this is for creation of 1 million objects and timings in milliseconds again:

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
Aerograph answered 1/7, 2011 at 14:6 Comment(4)
Thanks for your hard work, and testing, in this thread ! It's interesting to note that in .NET 4.0 'System.RunTime' has a method with the signature: 'CreateInstanceSlow(bool publicOnly, bool skipCheckThis, bool fillCache)' ... and 'Activator' has 25 overloads for the 'CreateInstance' method.Iver
@nawfal Why? What's wrong with that feature of the language? Or is it the top level "object" type?Bosnia
@MillieSmith yes the latter. Big fan of generics. I know some time its unavoidable.Boletus
I've tweaked a copy of this FastActivator version to use ConcurrentDictionary<TKey,TValue> with its method GetOrAdd(TKey Key, Func<TKey,TValue>)... Haven't tested its performance yet but I assume it will be closer in performance terms to this FastActivator than to Activator. This creates a dependency on .Net 4+.Boeotian
A
4

I'm putting this up as the so far best performant object creation factory so far using the current (2010-01-11) answers, according to my tests. I did notice that using cache works best when iterations are somewhere below 99,999. If you are going to load more than 99,999 it is best to not use cache. Because this could be the case I've created something that would allow you to use cache or not. My current project will sometimes load millions of records and at other times only load one. Anyways, I'm putting this out there to see what your thoughts are. Note that the code below is for ctor's that have 1 arg, one would have to create a similar factory for more than 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; 
    }
}
Atwell answered 11/1, 2010 at 22:7 Comment(3)
i haven't heard any comments for a while so i'll mark this as the best answer so far.Atwell
Excellent. Exactly what I was looking for exception not in VB :-). You are missing 2 semi colons in UseCache(). It would also make the example more complete if you added Comment and ObjectId classes.Joel
Really appreciate your hard work and testing in this thread ! Would be interesting to see how RepDbg's strategy performed in contrast to the other approaches here.Iver
K
1

I wrote this a while ago. It's using the .NET 3.5 Linq Expression trees rather than emitting IL, which is almost as fast, and more maintainable. It can take up to 4 constructor arguments.

Using any constructor arguments like you want to do might be a bit slower however due to looking up the constructor based on the argument types, but it's still much faster than with reflection. Also with IL emission there would have to be some lookup I think.

You have to specify the exact type which you want to construct as it's no IOC/DI container. Maybe you can extend and adapt it to your needs.

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

Have teh funz with it! :->

Kimball answered 8/1, 2010 at 16:56 Comment(4)
thanks! i'll give it a try. i never thought about linq expression trees.Atwell
I updated the Instantiator because I refactored and simplified it a lot. I noticed I can do without that dictionary and leverage the compiler's generics mechanisms instead for holding references to the compiled lambda expressions. We all make mistakes in the past and we learn with time. ;-) ... However, expression trees still take a while to compile, but once initialized, it comes as close as you can get to native code.Kimball
Looks like the update did speed it up in my tests. FROM: Instantiator<MyObject>.New(string,int): 00:00:00.0249225 TO: Instantiator<MyObject>.New(string,int): 00:00:00.0075623Atwell
This is the best answer here. Why wouldn't u use the generic pattern in InstantiatorImpl case as well? Why separately compiling tree for it?Boletus
L
1

I typically use the following method to completely avoid Activator.CreateInstance() in generic classes.

I require a Func<T> delegate to be passed to it it the constructor. The class will call this if it needs to instantiate a new instance of T. This will make instantiation as quick as in a non-generic class and is more simple and elegant IMO than a complex class that emits IL. Another upside (VS New T()) when using this method is that you can instantiate a class with a parametrized constructor.

Edit: I have updated the the code with a parametrized constructor example per BillW's request.

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();
    }
}
Lampyrid answered 1/7, 2011 at 18:0 Comment(1)
Interesting, and I upvoted this response. I'd appreciate it if you would show an example of how you'd use this strategy to create a new class with parameters.Iver
A
1

I had no idea that new T() was slow in a generic class. My problem is really something else though - if I knew what T was at compile time I'd be fine but I want a quick way to instantiate a class specified by configuration information (i.e. strings holding assembly / class names) at runtime. I use a cache of Type objects to load the assembly and locate the type in it only once, so the last hurdle is to quickly create instances, which was the subject of my previous post.

Anyhow, following from my finding that .NET 4.0 is quicker at this sort of thing I tested with a version of your example, calling each CreateNewInstxxx method 1,000,000 times. Timings in milliseconds:

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

So yes, .NET 4.0 is much faster than previously here too. The code generated by the compiler for the CreateNewInstStd() method looks like this in both cases so it seems the speedup is down to the improved Activator.CreateInstance<T>() method:

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

Edit: To add that performance is roughly the same when the GenericClass is constrained to reference types via this:

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

and in that case the compiler-generated code is then just this:

public T CreateNewInstStd()
{
    return Activator.CreateInstance<T>();
}
Aerograph answered 4/7, 2011 at 12:24 Comment(1)
I would like that CreateNewInstStd be just return default(T) != null ? default(T) : Activator.CreateInstance<T>() :)Boletus
E
0

I think the first thing you need to change is the CreateObject delegate. I would do it like this:

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

Then, here's a good way to generate a CreateObject delegate from a ConstructorInfo (using expression trees):

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

A full extended version of this compiler is in my personal repository. Feel free to browse whatever is there.

Update: repository link changed.

Eisenstein answered 8/1, 2010 at 20:39 Comment(4)
i may need more information on the best way to initiate CreateConstructor but this method seems to take the longest. From my tests (no cache) 999 instances of the same object takes a little over 2 seconds. This takes longer than Activator.CreateObject by quite a bit.Atwell
Activator.CreateInstance(Type)(string,int): 00:00:00.0139442 <br/>Instantiator<MyObject>.New(string,int): 00:00:00.0249225 FastObjectFactory.CreateObjec (empty cache): 00:00:00.0023258 FastObjectFactory.CreateObjec (cache full): 00:00:00.0000439 LateBoundMethodFactory.CreateConstructor: 00:00:02.8543006Atwell
This is essentially the same as the Instantiator but without the caching, that's why it doesn't perform well. The caching of the compiled expression is crucial. That's why 999 instances still isn't enough to outperform the Activator. :-/ Only after 10000 you will see the effect. So it's worth consideration if the application is long lived.Kimball
good to know as there will be times that there will be over 10,000. i'll do some tests with larger than 10k.Atwell
R
0

Has your profiler shown you that a large amount of time is spent in Activator.CreateInstance() (and not any constructor called by it) if you don't use the simple approach. If that is not the case, just use Activator.CreateInstance. (There seems to not be a generic CreateInstance() method with ctor arguments, but there is a non-generic one).

Rainger answered 10/1, 2010 at 2:3 Comment(0)
J
0

To follow is @thames answer converted to VB.NET.

//' Original source: https://mcmap.net/q/101722/-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

Note the use //' for comments allows SO code colorizer to work correctly. After pasting this code into your project replace //' with '.

Joel answered 3/11, 2010 at 20:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.