Я немного поэкспериментировал с этим, и как продолжение оригинальной статьи Мирона ( здесь ), я обнаружил, что .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