Entries
RSS 2.0

Comments
RSS 2.0

IronRuby and a Paradigm Shift at Microsoft

John Lam just announced that Microsoft will be accepting source code contributions for the IronRuby project! They’ve already impressed me by releasing the DLR, IronPython, and IronRuby under their Microsoft Permissive License, which is essentially just BSD with extra patent protections so no one sues them for a bajillion dollars. I have to admit, I didn’t think I’d see the day they’d accept community contributions to a project. More and more, it seems that Microsoft is starting to embrace open source. I don’t think they’re going to GPL the Windows source anytime soon, but opening the source of developer projects is a big step.

I can’t wait for the DLR… I’m hoping they can make IronPython and IronRuby first-class citizens in Visual Studio as well. I’m also interested in seeing what sort of fun DSL support the DLR can provide…

Inversion of Control and Generics

I stumbled across an interesting article today, in which the author describes an alternative take on inversion of control in .NET. I started to write a comment, but it got a little long-winded, so it became a blog post instead. :)

The author, Ralf, suggests that rather than injecting instances of dependencies, we should “inject” their type using a generic type argument. This means that instead of the standard dependency injection pattern:

   1: class MovieLister
   2: {
   3:   private IMovieFinder _finder;
   4:   public MovieLister(IMovieFinder finder)
   5:   {
   6:     _finder = finder;
   7:   }
   8: }

He suggests doing this:

   1: class MovieLister<TFinder>
   2:   where TFinder : IMovieFinder, new()
   3: {
   4:   private TFinder _finder;
   5:   public MovieLister()
   6:   {
   7:     _finder = new TFinder();
   8:   }
   9: }

This clearly has the advantage of not having to construct an IMovieFinder externally and then inject it into the MovieLister’s constructor. I have a few issues with this approach. First, by relying on the new() constraint, you’re only getting access to the parameterless constructor of the dependency. This means that if your dependency requires initialization logic, you’re out of luck. (And that rules out about 90% of dependencies.) Second, in order to create a MovieLister, you actually have to instantiate a MovieLister<MovieFinderImpl>. This means that any code that consumes your MovieLister must also know which implementation to inject to satisfy the IMovieFinder dependency. What happens if you want to change the implementation that you’re using for IMovieFinder? You have to find each place you’re creating a MovieLister and change the generic type argument.

This gets even scarier when you consider chained dependencies and the construction of an object graph. (This is where inversion of control containers shine, because they can automagically wire-up your objects for you without much coaxing.) What happens if your MovieFinder has a dependency, say on an IMovieRepository?

   1: class MovieLister<TFinder>
   2:   where TFinder : IMovieFinder, new()
   3: {
   4:   private TFinder _finder;
   5:   public MovieLister()
   6:   {
   7:     _finder = new TFinder();
   8:   }
   9: }
  10:  
  11: class MovieFinderImpl<TRepository> : IMovieFinder
  12:   where TRepository : IMovieRepository, new()
  13: {
  14:   private TRepository _repository;
  15:   public MovieFinderImpl()
  16:   {
  17:     _repository
 = new TRepository();
  18:   }
  19: }
  20:  
  21: class MovieRepositoryImpl : IMovieRepository
  22: { … }

Not too bad… but wait, how do we create a MovieLister now? Now, instead of having to instantiate a MovieLister<MovieFinderImpl>, we have to create a MovieLister<MovieFinderImpl<MovieRepositoryImpl>>. By using this idea of “type injection”, you’ve actually made each “parent” type dependent on not only its dependencies, but also on all of the dependencies of its dependencies (and their dependencies, and so on). In a real-life application, this would quickly become unmanageable.

Still, it’s an interesting idea. It would probably work in very basic scenarios, but it might make your code more difficult to maintain. I do agree with Ralf that using a service locator isn’t the best way to implement IoC, though. Instead, check out Titan! :)