Dapper.net的速度很快,最近看源碼,原來他orm的實現(xiàn)是通過編寫大量IL代碼實現(xiàn)的。
使用DynamicMethod,自己編織一個給實體賦值的方法。這種寫法效率很高,接近直接對屬性賦值。比使用反射賦值效率高10倍左右。
下面分別使用Emit,反射,直接賦值100000次來進(jìn)行對比測試。
<pre>
public static Action<T, object> EmitSetter<T>(string propertyName)
{
var type = typeof(T);
var dynamicMethod = new DynamicMethod("EmitCallable", null, new[] { type, typeof(object) }, type.Module);
var iLGenerator = dynamicMethod.GetILGenerator();
var callMethod = type.GetMethod("set_" + propertyName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
var parameterInfo = callMethod.GetParameters()[0];
var local = iLGenerator.DeclareLocal(parameterInfo.ParameterType, true);
iLGenerator.Emit(OpCodes.Ldarg_1);
if (parameterInfo.ParameterType.IsValueType)
{
// 如果是值類型,拆箱
iLGenerator.Emit(OpCodes.Unbox_Any, parameterInfo.ParameterType);
}
else
{
// 如果是引用類型,轉(zhuǎn)換
iLGenerator.Emit(OpCodes.Castclass, parameterInfo.ParameterType);
}
iLGenerator.Emit(OpCodes.Stloc, local);
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldloc, local);
iLGenerator.EmitCall(OpCodes.Callvirt, callMethod, null);
iLGenerator.Emit(OpCodes.Ret);
return dynamicMethod.CreateDelegate(typeof(Action<T, object>)) as Action<T, object>;
}
</pre>
下面是反射的寫法
<pre>
public static void ReflectSetter(object obj, string propertyName, object propertyValue)
{
var type = obj.GetType();
var propertyInfo = type.GetProperty(propertyName);
propertyInfo.SetValue(obj, propertyValue,null);
}
</pre>
我們新建一個簡單實體,添加一個屬性。
<pre>
public class Model
{
public string Name { get; set; }
}
</pre>
下面進(jìn)行測試
<pre>
private void Button_Click(object sender, RoutedEventArgs e)
{
Model model = new Model();
TimeSpan ts1;
DateTime time1 = DateTime.Now;
var emitSetter=EmitSetter<Model>("Name");
for (int i = 0; i < 100000; i++)
{
emitSetter(model, "測試");
}
DateTime time2 = DateTime.Now;
ts1 = time2 - time1;
Console.WriteLine("emit:"+ts1.TotalMilliseconds);
DateTime t3 = DateTime.Now;
for (int i = 0; i < 100000; i++)
{
ReflectSetter(model,"Name", "測試");
}
DateTime t4 = DateTime.Now;
ts1 = t4 - t3;
Console.WriteLine("reflect"+ts1.TotalMilliseconds);
DateTime t5 = DateTime.Now;
for (int i = 0; i < 100000; i++)
{
model.Name = "測試";
}
DateTime t6 = DateTime.Now;
ts1 = t6 - t5;
Console.WriteLine("直接賦值"+ts1.TotalMilliseconds);
}
</pre>
運行結(jié)果如下圖所示:

使用Emit執(zhí)行時間大概是直接賦值的兩倍,使用反射執(zhí)行時間大概是直接賦值的三十多倍。Emit效率是反射的十倍以上。