使用反射优化对象创建
本文关键字:对象 创建 优化 反射 | 更新日期: 2023-09-27 17:56:17
我正在尝试优化我们遗留代码中一个类的性能,该代码使用反射来创建各种视图。我宁愿我们根本不使用反射,但在短期内删除它不是一种选择。代码来自 MVC# 框架。在这里:
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);
}
}
我希望尽快实现这两种方法。我阅读了 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
我做了一些时间:
- :00.0225015
- :00.1232143
:00.3946555
:00.1476882
- 00:01.6342624
- 00:01.1591511
new Created()
: 00:00 Activator.CreateInstance<Created>()
: 00:00 (Created)CreateHelper.Create(typeof(Created))
: 00:00 new Created(i, i.ToString())
: 00:00 (Created)Activator.CreateInstance(typeof(Created), new object[]{ i, i.ToString() })
: 00:(Created)CreateHelper.Create(typeof(Created), new object[] {i, i.ToString()})
: 00:更新 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;
}
}
总结
对于默认构造函数情况,以下是摘要:
- :00.3946555
- :00.0225015
- :00.1232143
- :00.1165000
- :00.1131143
(Created)CreateHelper.Create(typeof(Created))
: 00:00 new Created()
: 00:00 Activator.CreateInstance<Created>()
: 00:00 DynamicMethod
: 00:00 Expression.New
: 00:00 首先,您可能应该禁用该特定测试的垃圾回收器。它可能会妨碍您的结果。或者,您可以将所有创建的实例放入一个数组中。我不确定将ToString()
称为其中的一部分是否有帮助。
你对花哨的构造函数代码的计划不一定是正确的计划。构造函数查找本身非常慢。应使用 Type
键将委托缓存在字典中。大多数 IoC 容器会自动执行此操作(缓存和构造)。我认为使用其中之一在您的情况下会证明是有价值的。事实上,较新的 JSON 框架还缓存构造函数信息,以便快速创建对象。也许像 Json.Net 或ServiceStack.Text这样的东西会有所帮助。
您正在构建多少种不同的类型?(我知道你的例子只显示了一个。
我不确定您在动态方法上的参数是否正确。这段代码(如下)对我来说是稳定的。不要在值类型或数组上调用它。
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