最近學習了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的效率果然是高。學會了高效的創建泛型對象的一種方式。