Creating Lazy<T> objects via Reflection and Expressions
Last week, I’ve been playing with a heavily customized version of TinyIoC. While it’s quite mature and feature-rich, I wanted to make it support MEF’s attributed model. One of the main issues with that is, obviously, resolving cyclic dependencies where Foo depends on IBar, and Bar depends on IFoo. The easier solution is to just make all imports Lazy<T> and make sure to never call an imported property in the constructor. Now that that’s been decided, how do you construct Lazy<T> with reflection? Let’s formalize the problem with some code :)
So basically, we have some internal factory method, that we want to use to construct a Lazy<T> and return via ConstructLazy. Obviously, if we could use generics, it would be straightforward, but there are some cases (like with TinyIoC) where generics are not feasible. So, we’re left with Expressions/Reflection to achieve that task. Let’s see the code:
Let’s break it down a little :)
- Expression.Call requires instance (Expression.Constant(this)), the MethodInfo (factoryMethod) and method arguments in the form of expressions.
- We need Expression.Cast to cast from object to desiredType, or the Lazy constructor will throw runtime exception.
- We need the Expression.Lambda to create the Func<TDesiredType> to pass in the Lazy<TDesiredType> constructor.
- And finally, we construct the Lazy<TDesiredType> via Activator.CreateInstance passing the compiled lambda as the only argument.
That’s it — a reflection/expression mechanism to create Lazy<T> without generics. Should you use it? It depends. Probably not, unless you absolutely have to. In the case with TinyIoC, I wanted to create an automated mechanism to construct my objects and that would be impossible to do without it. Obviously it has performance implications, but I was okay with sacrificing performance for convenience. So always weigh your options and choose the strategy that best fits your needs. If you are okay with the performance hit, TinyIoC with MEF attributed model is quite easy to use and convenient. But if you are not, perhaps you should look at a static ServiceLocator pattern :)