c# - new - Creación rápida de objetos en lugar de Activator.CreateInstance(tipo)
new typeof (5)
El problema es si va a llamar CreateInstance una y otra vez directamente en lugar de guardar el resultado en alguna parte y usar ese resultado una y otra vez, probablemente debería seguir adelante y almacenarlo en caché.
internal static class DelegateStore<T>{
internal static IDictionnary<string, Func<T>> Store =new ConcurrentDictionary<string,Func<T>>();
}
public static T CreateInstance<T>(Type objType) where T : class
{
Func<T> returnFunc;
if(!DelegateStore<T>.Store.TryGetValue(objType.FullName, out returnFunc)){
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);
returnFunc =(Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
DelegateStore<T>.Store[objType.FullName]=returnfunc;
}
return returnFunc();
}
Estoy tratando de mejorar el rendimiento de nuestra aplicación. Tenemos muchas llamadas Activator.CreateInstance que están causando algo de dolor.
Instalamos muchas clases basadas en una interfaz (ITabDocument) y después de mirar pensé en usar este código:
El código no es mejor (de hecho, es un poco más lento) que usar el código Activator.CreateInstance que teníamos.
public static Func<T> CreateInstance<T>(Type objType) where T : class, new()
{
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);
return (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
}
Me pregunto por qué es esto, todo lo que hago es:
ITabDocument document = CreateInstance<ITabDocument>(Type.GetType("[Company].Something"));
¿Hay alguna forma mejor de crear objetos que ayuden con lo anterior? Es un poco difícil cuando no estás seguro del tipo concreto.
Esto podría ayudar: No use Activator.CreateInstance o ConstructorInfo.Invoke, use expresiones compiladas lambda :
// Make a NewExpression that calls the ctor with the args we just created NewExpression newExp = Expression.New(ctor, argsExp); // Create a lambda with the New expression as body and our param object[] as arg LambdaExpression lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param); // Compile it ObjectActivator compiled = (ObjectActivator)lambda.Compile();
Hice algunos benchmarking entre estos (escribiría los mínimos detalles):
public static T Instance() //~1800 ms
{
return new T();
}
public static T Instance() //~1800 ms
{
return new Activator.CreateInstance<T>();
}
public static readonly Func<T> Instance = () => new T(); //~1800 ms
public static readonly Func<T> Instance = () =>
Activator.CreateInstance<T>(); //~1800 ms
//works for types with no default constructor as well
public static readonly Func<T> Instance = () =>
(T)FormatterServices.GetUninitializedObject(typeof(T)); //~2000 ms
public static readonly Func<T> Instance =
Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();
//~50 ms for classes and ~100 ms for structs
Como dice CD, la expresión compilada es la más rápida y por un gran margen. Todos los métodos excepto (T)FormatterServices.GetUninitializedObject(typeof(T))
funcionan solo para los tipos con el constructor predeterminado.
Y el almacenamiento en caché del delegado resultante compilado es trivial cuando tienes una clase estática por tipo genérico. Me gusta:
public static class New<T> where T : new()
{
public static readonly Func<T> Instance = Expression.Lambda<Func<T>>
(
Expression.New(typeof(T))
).Compile();
}
Tenga en cuenta la new
restricción. Llamar a cualquier cosa
MyType me = New<MyType>.Instance();
Excepto por la primera vez que la clase se carga en la memoria, la ejecución será más rápida.
Para tener una clase que maneje ambos tipos con el constructor predeterminado y sin, tomé un enfoque híbrido, desde aquí :
public static class New<T>
{
public static readonly Func<T> Instance = Creator();
static Func<T> Creator()
{
Type t = typeof(T);
if (t == typeof(string))
return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();
if (t.HasDefaultConstructor())
return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();
return () => (T)FormatterServices.GetUninitializedObject(t);
}
}
public static bool HasDefaultConstructor(this Type t)
{
return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}
Manejará los tipos de valor también de manera eficiente.
Tenga en cuenta que (T)FormatterServices.GetUninitializedObject(t)
fallará para la string
. Por lo tanto, el manejo especial de la cuerda está en su lugar para devolver una cuerda vacía.
Método genérico para construir delegados, llamando al constructor directamente. Busca automáticamente el constructor en un tipo dado con la firma del tipo de delegado y crea un delegado de ese tipo. Codifique aquí:
/// <summary>
/// Reflective object construction helper.
/// All methods are thread safe.
/// </summary>
public static class Constructor
{
/// <summary>
/// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType.
/// Instance is casted to delegateTypes''s return type.
/// Delegate''s return type must be assignable from instanceType.
/// </summary>
/// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param>
/// <param name="instanceType">Type of instance to be constructed.</param>
/// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns>
public static Delegate Compile(Type delegateType,Type instanceType)
{
if (!typeof(Delegate).IsAssignableFrom(delegateType))
{
throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType");
}
var invoke = delegateType.GetMethod("Invoke");
var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray();
var resultType = invoke.ReturnType;
if(!resultType.IsAssignableFrom(instanceType))
{
throw new ArgumentException(String.Format("Delegate''s return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName));
}
var ctor = instanceType.GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
if(ctor == null)
{
throw new ArgumentException("Can''t find constructor with delegate''s signature","instanceType");
}
var parapeters = parameterTypes.Select(Expression.Parameter).ToArray();
var newExpression = Expression.Lambda(delegateType,
Expression.Convert(Expression.New(ctor, parapeters), resultType),
parapeters);
var @delegate = newExpression.Compile();
return @delegate;
}
public static TDelegate Compile<TDelegate>(Type instanceType)
{
return (TDelegate) (object) Compile(typeof (TDelegate), instanceType);
}
}
es parte de las fuentes del proyecto Yappi . Utilizándolo, puede construir un delegado llamando a cualquier constructor de un tipo determinado, incluido el constructor con parámetros (excepto los parámetros ref y out).
Uso de muestra:
var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>));
var list = newList(100);
Después de la construcción del delegado, guárdelo en algún lugar del diccionario estático o en el campo estático de la clase con un parámetro genérico. No construyas un nuevo delegado cada vez. Use un delegado para construir múltiples instancias de tipo dado.
Probablemente estés recibiendo algunos gastos generales de la generación del mismo código.
ILGenerator
crea dinámicamente código para la fábrica.
Cree una especie de mapa o Dictionary
de tipos que ya haya utilizado y mantenga el método de fábrica creado para ese tipo.