This is a third companion post to Scott Guthrie’s ASP.NET MVC 2: Strongly Typed Html Helpers.
Today instead of reading code, I’d thought I’d write some. I threw together a class which does the analysis performed by the FastTrack class in ASP.NET MVC2. To recap – the FastTrack class:
- decides if an expression can be “fast tracked”
- if it can – builds, compiles and caches it
- returns the cached expression in future calls
This code demonstrates the bit that does the deciding, and gives us something to play around with.
using System; using System.Linq.Expressions; namespace MvcHarness { public class FastTrackAnalyser<TModel> { public void Analyse<TValue>(Expression<Func<TModel, TValue>> lambda) { ParameterExpression modelParameter = lambda.Parameters[0]; Expression body = lambda.Body; if (modelParameter == body) { Console.WriteLine("{0} is a function returning its own argument", lambda); return; } ConstantExpression constantExpression = body as ConstantExpression; if (constantExpression != null) { Console.WriteLine("{0} is a function returning a constant value", lambda); return; } MemberExpression memberExpression = body as MemberExpression; if (memberExpression != null) { if (memberExpression.Expression == null) { Console.WriteLine("{0} is a function returning a static member of either the model or another type", lambda); return; } if (memberExpression.Expression == modelParameter) { Console.WriteLine("{0} is a function returning a member of the model", lambda); return; } ConstantExpression constantExpression2 = memberExpression.Expression as ConstantExpression; if (constantExpression2 != null) { Console.WriteLine("{0} is a function representing a captured local variable", lambda); return; } } Console.WriteLine("{0} cannot be fast tracked", lambda); } } }
I called it like this (with output inline in the comments):
using System; namespace MvcHarness { class Program { static void Main(string[] args) { FastTrackAnalyser<string> analyser = new FastTrackAnalyser<string>(); analyser.Analyse(x => x); // outputs "x => x is a function returning its own argument" analyser.Analyse(x => 999); // outputs "x => 999 is a function returning a constant value" analyser.Analyse(x => String.Empty); // outputs "x => String.Empty is a function returning a static member of either the model or another type" analyser.Analyse(x => x.Length); // outputs "x => x.Length is a function returning a member of the model" string name = "Joe Field"; analyser.Analyse(x => name); // outputs "x => value(MvcHarness.Program+<>c__DisplayClass0).name is a function representing a captured local variable" analyser.Analyse(x => name.Length); // outputs "x => value(MvcHarness.Program+<>c__DisplayClass0).name.Length cannot be fast tracked" } } }
The really interesting calls are the last two. They are analysing expressions which use captured local variables. The first one succeeds, but the second fails because it attempts to access a member of the variable.