跳过ASP.NET MVC中所有的路由映射和各种有的没的缓存机制,一个个请求从发送到处理,最终是由Action去执行的,而在ASP.NET MVC中,Action的最终执行者叫做ActionMethodDispacher。
在这篇文章中,我想从无到有地建立一个Dispatcher,顺便窥探一下ASP.NET MVC中Action的执行机制。
在没有看源码之前,我觉得这个Dispatcher没有什么了不起,不就是个动态函数调用嘛,使用反射调用不就就行了,几行代码随便搞定:
public class ReflectionDispatcher
{
public MethodInfo MethodInfo {get;set;}
public ReflectionDispatcher(MethodInfo methodInfo)
{
MethodInfo = methodInfo;
}
public object Execute(TestClass instance, object[] parameters)
{
return MethodInfo.Invoke(instance, parameters);
}
}
多么简单,简直就是一目了然,为了这么点功能,建一个类简直就是多余!
但是,如果ASP.NET MVC中的ActionMethodDispacher也是这样实现的话,那么...嘿嘿,估计会被人骂的很惨吧。
原因是这个ReflectDispacher的性能非常低下,不是因为代码写得太差,只是因为反射调用先天不足,本来就有性能的瓶颈。
关于如何高效动态调用函数,这里就不多说了,详见:http://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker
下面我试着仿照ASP.NET MVC的ActionMethodDispacher来建一个性能高点的Dispacher,至于性能可以高多少,不妨等下做个小试验。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;
namespace MvcConsole
{
public class ActionMethodDispatcher
{
private delegate object ActionExecutor(TestClass instance, object[] parameters);
private delegate void VoidActionExecutor(TestClass instance, object[] parameters);
private ActionExecutor _executor;
private static ActionExecutor BuildActionExecutor(MethodInfo method)
{
ParameterExpression instanceParameter = Expression.Parameter(typeof(TestClass), "instance");
ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
List<Expression> parameters = new List<Expression>();
ParameterInfo[] parameterInfos = method.GetParameters();
for (int i = 0; i < parameterInfos.Length; i++)
{
ParameterInfo paramInfo = parameterInfos[i];
BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);
parameters.Add(valueCast);
}
MethodCallExpression methodCall = Expression.Call(instanceParameter, method, parameters);
return
methodCall.Type == typeof(void) ?
WrapVoidAction(Expression.Lambda<VoidActionExecutor>(methodCall, instanceParameter, parametersParameter).Compile()) :
Expression.Lambda<ActionExecutor>(methodCall, instanceParameter, parametersParameter).Compile();
}
public ActionMethodDispatcher(MethodInfo methodInfo)
{
_executor = BuildActionExecutor(methodInfo);
}
public object Execute(TestClass instance, object[] parameters)
{
return _executor(instance, parameters);
}
private static ActionExecutor WrapVoidAction(VoidActionExecutor executor)
{
return (instance, parameters) =>
{
executor(instance, parameters);
return null;
};
}
}
}
从这个代码中,我们明显可以看到,这个Dispacher貌似要“胖”很多。
下面就来比较下两个Dispatcher的性能:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;
using System.Diagnostics;
namespace MvcConsole
{
public class TestClass
{
public String State { get; set; }
public TestClass()
{
State = "Init";
}
public void PrintObjects(String para1, String para2)
{
}
}
class Program
{
static void Main(string[] args)
{
Stopwatch stopWatch = new Stopwatch();
int times = 10000000;
var dispatcher = new ActionMethodDispatcher(typeof(TestClass).GetMethod("PrintObjects"));
var testClass = new TestClass();
testClass.State = "Expression Call";
stopWatch.Start();
for (int i = 0; i < times; i++)
{
dispatcher.Execute(testClass, new object[] { "Hello", "World" });
}
stopWatch.Stop();
Console.WriteLine("Expression Call use: " + stopWatch.Elapsed);
var mydispatcher = new RelectionDispatcher(typeof(TestClass).GetMethod("PrintObjects"));
testClass.State = "Reflect Call";
stopWatch.Reset();
stopWatch.Start();
for (int i = 0; i < times; i++)
{
mydispatcher.Execute(testClass, new object[] { "Hello", "World" });
}
stopWatch.Stop();
Console.WriteLine("Reflect Call use: " + stopWatch.Elapsed);
}
}
}
运行结果:
Expression Call use: 00:00:00.3916032
Reflect Call use: 00:00:09.4091986
ActionMethodDispatcher的性能要比ReflectDispatcher的性能高20多倍。好吧,这次Reflect的调用真是完败啊,为了这20多倍的性能,我们就是再多写20多倍的代码也在所不惜啊。