反射是一种很重要的技术,然而它与直接调用相比性能要慢很多,因此如何优化反射性能也就成为一个不得不面对的问题。 目前最常见的优化反射性能的方法就是采用委托:用委托的方式调用需要反射调用的方法(或者属性、字段)。
那么如何得到委托呢? 目前最常见也就是二种方法:Emit, ExpressionTree 。其中ExpressionTree可认为是Emit方法的简化版本, 所以Emit是最根本的方法,它采用在运行时动态构造一段IL代码来包装需要反射调用的代码, 这段动态生成的代码满足某个委托的签名,因此最后可以采用委托的方式代替反射调用。
用Emit方法优化反射
如果我们需要设计自己的数据访问层,那么就需要动态创建所有的数据实体对象,尤其是还要为每个数据实体对象的属性赋值, 这里就要涉及用反射的方法对属性执行写操作,为了优化这种反射场景的性能,我们可以用下面的方法来实现:
public delegate voidSetValueDelegate(objecttarget, objectarg);
public static classDynamicMethodFactory{
public staticSetValueDelegateCreatePropertySetter(PropertyInfoproperty)
{
if( property ==null)
throw newArgumentNullException("property");
if( !property.CanWrite )
return null;
MethodInfosetMethod =property.GetSetMethod(true);
DynamicMethoddm =newDynamicMethod("PropertySetter", null,
newType[] { typeof(object), typeof(object) },
property.DeclaringType, true);
ILGeneratoril =dm.GetILGenerator();
if( !setMethod.IsStatic ) {
il.Emit(OpCodes.Ldarg_0);
}
il.Emit(OpCodes.Ldarg_1);
EmitCastToReference(il, property.PropertyType);
if( !setMethod.IsStatic && !property.DeclaringType.IsValueType ) {
il.EmitCall(OpCodes.Callvirt, setMethod, null);
}
elseil.EmitCall(OpCodes.Call, setMethod, null);
il.Emit(OpCodes.Ret);
return(SetValueDelegate)dm.CreateDelegate(typeof(SetValueDelegate));
}
private static voidEmitCastToReference(ILGeneratoril, Typetype)
{
if( type.IsValueType )
il.Emit(OpCodes.Unbox_Any, type);
elseil.Emit(OpCodes.Castclass, type);
}
}
现在可以用下面的测试代码检验委托调用带来的性能改进:
Console.WriteLine(System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion());
intcount =1000000;
OrderInfotestObj =newOrderInfo();
PropertyInfopropInfo =typeof(OrderInfo).GetProperty("OrderID");
Console.Write("直接访问花费时间: ");
Stopwatchwatch1 =Stopwatch.StartNew();
for( inti =0; i
testObj.OrderID =123;
watch1.Stop();
Console.WriteLine(watch1.Elapsed.ToString());
SetValueDelegatesetter2 =DynamicMethodFactory.CreatePropertySetter(propInfo);
Console.Write("EmitSet花费时间: ");
Stopwatchwatch2 =Stopwatch.StartNew();
for( inti =0; i
setter2(testObj, 123);
watch2.Stop();
Console.WriteLine(watch2.Elapsed.ToString());
Console.Write("纯反射花费时间: ");
Stopwatchwatch3 =Stopwatch.StartNew();
for( inti =0; i
propInfo.SetValue(testObj, 123, null);
watch3.Stop();
Console.WriteLine(watch3.Elapsed.ToString());
Console.WriteLine("-------------------");
Console.WriteLine("{0} / {1} = {2}",
watch3.Elapsed.ToString(),
watch1.Elapsed.ToString(),
watch3.Elapsed.TotalMilliseconds /watch1.Elapsed.TotalMilliseconds);
Console.WriteLine("{0} / {1} = {2}",
watch3.Elapsed.ToString(),
watch2.Elapsed.ToString(),
watch3.Elapsed.TotalMilliseconds /watch2.Elapsed.TotalMilliseconds);
Console.WriteLine("{0} / {1} = {2}",
watch2.Elapsed.ToString(),
watch1.Elapsed.ToString(),
watch2.Elapsed.TotalMilliseconds /watch1.Elapsed.TotalMilliseconds);
我用VS2008 (.net 3.5 , CLR 2.0) 测试可以得到以下结果: