最近学习了msil,发现了很多好玩的,今天介绍一个用IL来创建对象的方式
1.最常见的两种创建对象方式
public static T Create<T>() where T : new()
{
return new T();
}
public static object CreateNative()
{
return new object();
}
写一个测试帮助方法简单的测试下这两个方法的执行时间的长短:
public static void Measure(string what, int reps, Action action)
{
action(); //warm up
double[] results = new double[reps];
for (int i = 0; i < reps; i++)
{
Stopwatch sw = Stopwatch.StartNew();
action();
results[i] = sw.Elapsed.TotalMilliseconds;
}
Console.WriteLine("{0} - AVG = {1}, MIN = {2}, MAX = {3}", what,
results.Average(), results.Min(), results.Max());
}
调用测试方法:
int reps = 5;
int its = 100000;
Measure("create", reps, () =>
{
for (int i = 0; i < its; i++)
{
Create<object>();
}
});
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Measure("createNative", reps, () =>
{
for (int i = 0; i < its; i++)
{
CreateNative();
}
});
执行结果:
可以通过测试结果看出来本地方法创建的比泛型方式创建的消耗的时间短,这是为什么。用工具查看生成的il就可以发现为什么了。
泛型方式生成的IL如下:
本地方式的生成IL如下:
可以看出泛型方式生成的IL里面调用了Activator.CreateInstance方法,而本地方式而直接new一个对象。所以本地方式的生成对象要比泛型方式用时短。既然这样直接通过Activator.CreateInstance 生成对象呢。
2.Activator.CreateInstance方式生成对象
public static object CreateReflect(Type type)
{
return Activator.CreateInstance(type);
}
现在再来比较这三种的生成方式的用时长短,用同样的方式调用CreateReflect,得到结果如下:
从结果上看可以看出最快的是本地直接new,第二快是通过Activator.CreateInstance,最慢的则是泛型实例化创建对象。
在工作中泛型创建对象很常见。如何解决泛型创建对象慢的问题呢?
3.使用IL来创建泛型对象
public class CreationHelper<T> where T : new()
{
public static Func<T> objCreator = null;
public static T New()
{
if (objCreator == null)
{
Type objectType = typeof(T);
ConstructorInfo defaultCtor = objectType.GetConstructor(new Type[] { });
DynamicMethod dynMethod = new DynamicMethod(
name: string.Format("_{0:N}", Guid.NewGuid()),
returnType: objectType,
parameterTypes: null);
var gen = dynMethod.GetILGenerator();
gen.Emit(OpCodes.Newobj, defaultCtor);
gen.Emit(OpCodes.Ret);
objCreator = dynMethod.CreateDelegate(typeof(Func<T>)) as Func<T>;
}
return objCreator();
}
}
用以上相同的方式来测试,测试代码:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Measure("DynamicCreate", reps, () =>
{
for (int i = 0; i < its; i++)
{
CreationHelper<object>.New();
}
});
测试结果如下:
结果一目了然,IL方式创建对象的用时在本地实例化和Activator.CreateInstance之间,只比本地实例化稍慢。所以泛型实例化可以考虑这种方式,可以提升泛型实例化的效率。
4.总结
本文介绍了c#创建对象的4种方式,简单的比较了这四种创建效率。说明了IL的效率果然是高。学会了高效的创建泛型对象的一种方式。