我正在尝试在使用反射来创建各种视图的遗留代码中优化类的性能。我宁愿我们根本不使用反射,但在短期内删除它不是一种选择。代码来自MVC#framework。这是:
public class CreateHelper
{
#region Documentation
/// <summary>
/// Creates an object of specified type.
/// </summary>
#endregion
public static object Create(Type t)
{
return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
}
#region Documentation
/// <summary>
/// Creates an object of specified type with parameters passed to the constructor.
/// </summary>
#endregion
public static object Create(Type t, params object[] parameters)
{
Type[] paramTypes = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
paramTypes[i] = parameters[i].GetType();
return t.GetConstructor(paramTypes).Invoke(parameters);
}
}
我希望尽快实现这两种方法。我在对象创建优化中阅读了这个great article by Ayende,并试图为我的目的修改他的例子,但是我对IL的知识是不存在的。
我得到VerificationException
操作可能会破坏Create
方法中的运行时。。有谁知道这是什么问题?我可以使用这种方法更快的实现吗?这是我的尝试:
public class Created
{
public int Num;
public string Name;
public Created()
{
}
public Created(int num, string name)
{
this.Num = num;
this.Name = name;
}
}
public class CreateHelper
{
private delegate object CreateCtor();
private static CreateCtor createdCtorDelegate;
#region Documentation
/// <summary>
/// Creates an object of specified type.
/// </summary>
#endregion
public static object Create(Type t)
{
var ctor = t.GetConstructor(new Type[] { });
var method = new DynamicMethod("CreateIntance", t, new Type[] { typeof(object[]) });
var gen = method.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);//arr
gen.Emit(OpCodes.Call, ctor);// new Created
gen.Emit(OpCodes.Ret);
createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));
return createdCtorDelegate(); // <=== VerificationException Operation could destabilize the runtime.
}
#region Documentation
/// <summary>
/// Creates an object of specified type with parameters passed to the constructor.
/// </summary>
#endregion
public static object Create(Type t, params object[] parameters)
{
Type[] paramTypes = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
paramTypes[i] = parameters[i].GetType();
return t.GetConstructor(paramTypes).Invoke(parameters);
}
}
然后我使用这样的类:
class Program
{
private static Created CreateInstance()
{
return (Created)CreateHelper.Create(typeof(Created));
//return new Created();
}
static void Main(string[] args)
{
int iterations = 1000000;
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
CreateInstance();
}
Console.WriteLine(watch.Elapsed);
Console.ReadLine();
}
}
更新1
我做了一些时间:
new Created()
:00:00:00.0225015 Activator.CreateInstance<Created>()
:00:00:00.1232143 (Created)CreateHelper.Create(typeof(Created))
:00:00:00.3946555
new Created(i, i.ToString())
:00:00:00.1476882
(Created)Activator.CreateInstance(typeof(Created), new object[]{ i, i.ToString() })
:00:00:01.6342624 (Created)CreateHelper.Create(typeof(Created), new object[] {i, i.ToString()})
:00:00:01.1591511 更新2
对于默认的构造函数情况,@ Brannon建议的解决方案有效,但获得的时间是00:00:00.1165000,这不是一个巨大的改进。这是:
public class CreateHelper
{
private delegate object DefaultConstructor();
private static readonly ConcurrentDictionary<Type, DefaultConstructor> DefaultConstructors = new ConcurrentDictionary<Type, DefaultConstructor>();
#region Documentation
/// <summary>
/// Creates an object of specified type.
/// </summary>
#endregion
public static object Create(Type t)
{
DefaultConstructor defaultConstructorDelegate;
if (!DefaultConstructors.TryGetValue(t, out defaultConstructorDelegate))
{
var ctor = t.GetConstructor(Type.EmptyTypes);
var method = new DynamicMethod("CreateIntance", t, Type.EmptyTypes);
var gen = method.GetILGenerator();
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Newobj, ctor);
gen.Emit(OpCodes.Ret);
defaultConstructorDelegate = (DefaultConstructor)method.CreateDelegate(typeof(DefaultConstructor));
DefaultConstructors[t] = defaultConstructorDelegate;
}
return defaultConstructorDelegate.Invoke();
}
}
更新3
使用Expression.New
的编译表达式也产生了非常好的结果(00:00:00.1166022)。这是代码:
public class CreateHelper
{
private static readonly ConcurrentDictionary<Type, Func<object>> DefaultConstructors = new ConcurrentDictionary<Type, Func<object>>();
#region Documentation
/// <summary>
/// Creates an object of specified type.
/// </summary>
#endregion
public static object Create(Type t)
{
Func<object> defaultConstructor;
if (!DefaultConstructors.TryGetValue(t, out defaultConstructor))
{
var ctor = t.GetConstructor(Type.EmptyTypes);
if (ctor == null)
{
throw new ArgumentException("Unsupported constructor for type " + t);
}
var constructorExpression = Expression.New(ctor);
var lambda = Expression.Lambda<Func<Created>>(constructorExpression);
defaultConstructor = lambda.Compile();
DefaultConstructors[t] = defaultConstructor;
}
return defaultConstructor.Invoke();
}
#region Documentation
/// <summary>
/// Creates an object of specified type with parameters passed to the constructor.
/// </summary>
#endregion
public static object Create(Type t, params object[] parameters)
{
return null;
}
}
摘要
对于默认构造函数的情况,这里是摘要:
(Created)CreateHelper.Create(typeof(Created))
:00:00:00.3946555 new Created()
:00:00:00.0225015 Activator.CreateInstance<Created>()
:00:00:00.1232143 DynamicMethod
:00:00:00.1165000 Expression.New
:00:00:00.1131143 答案 0 :(得分:1)
首先,您应该为该特定测试禁用垃圾收集器。它可能会妨碍您的结果。或者您可以将所有创建的实例放入数组中。我不确定将ToString()
作为其中的一部分也是有帮助的。
您对花哨的构造函数代码的计划不一定是正确的计划。构造函数查找本身很慢。您应该使用Type
密钥将代理缓存在字典中。大多数IoC容器会自动为您执行此操作(缓存和构建)。我认为使用其中一种在你的情况下证明是有价值的。实际上,较新的JSON框架还会缓存构造函数信息以便快速创建对象。也许像Json.Net或ServiceStack.Text这样的东西会有所帮助。
您要构建多少种不同的类型? (我知道你的例子只显示一个。)
我不确定DynamicMethod上的参数是否正确。这段代码(下面)对我来说很稳定。不要在值类型或数组上调用它。
DynamicMethod dm = new DynamicMethod("MyCtor", type, Type.EmptyTypes, typeof(ClassFactory).Module, true);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Nop);
ilgen.Emit(OpCodes.Newobj, ci);
ilgen.Emit(OpCodes.Ret);
CtorDelegate del = ((CtorDelegate) dm.CreateDelegate(typeof(CtorDelegate)));
return del.Invoke(); // could cache del in a dictionary