Dependency Injection Via the C# 'new' Keyword

by msimpson 11/19/2009 5:05:00 PM
For the last year or two I've been getting into the principle of Inversion of Control (IoC) as a way of decoupling pieces of my applications.  While I think the name 'Inversion of Control' is a pretty poor choice because you can't really infer much from it, the principle itself is valuable.  In my case, I've been exploring a specific technique for IoC called Dependency Injection (DI).

To understand what DI can do for you, you first need to understand the problem it's trying to solve.  In any reasonably complex piece of object-oriented software, there will be multiple classes organized into layers and/or tiers.  If the programming language is strongly-typed, then ideally, the developers will have formalized the interactions between them using interfaces, which define chunks of abstract functionality.  These interfaces allow calling code to be agnostic of the actual type of the object they're calling, which in turn allows those physical implementations of the called code to be swapped out without the calling code ever being the wiser.

Most OOP developers will agree that the above is a huge step towards decoupling the codebase.  Interfaces are old hat, right?  However as the application grows, another type of dependency manifests itself - construction logic.  The calling code needs to obtain an object that implements the interface it's interested in.  Now the calling code is not supposed to care what type of object that actually is, but if it's going to create it, it's going to have to know, since an interface cannot be instantiated directly.  And poof! - the glorious agnosticism we thought our interface was granting us has disappeared.

Now extrapolate a little further, and imagine that the object we're trying to create has its own dependencies, which we need to pass into it when we create it.  And imagine that those classes also have their own dependencies, and ad infinitum.  Suddenly our poor calling code, which really only wants to call IFoo.Bar(), now has to know how to create the entire universe before it can do anything.  Now imagine you've got a dozen classes that need to call IFoo.Bar() - that means a dozen classes that need to know how to create the universe.  If you decide to change the constructor for the MyFoo class you're calling, you need to visit each of those dozen calling classes to update their construction logic.  And what if you want to change from MyFoo to YourFoo?  Same thing. Refactoring tools are getting better all the time, but they're not that good yet, and may never be.

The lesson?  Construction logic is just as much of a dependency as a hard reference to a concrete type, and we need to externalize it.  

The traditional way to do that is through the use of factory classes.  These are classes designed expressly to create new instances of objects (probably objects implementing a particular interface), and returning them to the caller.  Factories do help; but they don't go far enough.  Chances are your application will need many different factories, say one for each interface.  Construction logic will be sprinkled across all your factory classes.  The calling code needs to know how to access each one, and they may differ in how they're called and where they live.  And you have to write the factories.

Enter Dependency Injection.  This is typically implemented as a library that provides the following:

  • A simple interface for getting instances of types based on certain criteria;
  • A way to easily configure what types get returned for what criteria;
  • A 'super-factory' to create objects and return them;
  • A way to specify to the factory what dependencies should be satisfied, and how, before returning newly-created objects.

A DI engine is typically called a 'container', but I called it a 'super-factory' above because it does what a normal factory does, but provides a ton of extra features.  If configured properly, it can not only instantiate MyFoo, but when it does, it will satisfy MyFoo's dependencies, and those classes' dependencies, and so on, all before returning your MyFoo to you.  You can configure switches to change out whole sets of types, for example 'release', 'debug' and 'unit test' (where you might want to substitute a mock data access layer instead of the real one).  It can call a method of yours to create objects, if you want control over how the objects are created but want to centralize that logic.  It can decide on-the-fly between multiple available implementations of an interface, based on context available at runtime.  It can manage singletons for you, without your having to write your classes as singletons.  And you don't have to write the factory, just configure it.

There are many dependency injection libraries available, and they're all pretty much equivalent in terms of functionality, but they differ in how they're configured.  I've been using Ninject, which has cool cartoon ninjas on its web site, but more importantly uses a type-safe 'fluent' interface for configuration.  Some other libraries use XML configuration files instead.  Take your pick.

With Ninject, assuming I have a reference to the Ninject kernel, if I want to get an object that implements IFoo, I can do this:

    var foo = kernel.Get<IFoo>();
    
Ninject sees that I want an IFoo, looks at its configuration and sees that the IFoo is bound to the MyFoo class, creates an instance of MyFoo and returns it to me.  My class has taken a dependency on Ninject, but successfully avoided any dependency on MyFoo, MyFoo's dependencies, or any other concrete class Ninject is configured to provide.  Slick!

In case you're interested, the binding configuration is like so:

    Bind<IFoo>().To<MyFoo>();
    
This code is normally called once, when the app starts up.  This is a very simple example.

This is all well and good, but looking back at the call to IKernel.Get<T>(), I can see room for improvement.  IKernel.Get<T>() is not as clean as new.  How do you enforce that developers don't use the new keyword and create objects directly?  And what if you have a legacy application that doesn't use a DI container?

It seems to me that dependency injection is something that ought to be present in the language itself.  Ideally, I'd like to co-opt the new keyword and make it do dependency injection.  This would have profound implications for the language as a whole.  It would need to implemented in the compiler, affecting type inference, and it might prevent the compiler from making certain optimizations and type safety checks.  I admit I have not thought through all the possible implications, and I don't really have the expertise to do so, not being on the C# compiler team.

But here's how I imagine it:

  • Microsoft would provide a syntax and API for obtaining objects, equivalent to the Get<T>() method above and its siblings.  I imagine both a 'language-integrated' syntax based on new, and a separate programmatic API, so you could use either.  
  • Microsoft would provide a DI implementation, based on the provider model, that by default simply instantiates the requested type and returns it.  DI providers could be implemented by third parties and hooked in via configuration.
  • By default, the new keyword would do what it does now - simply instantiate an object, without using DI at all, so as not to affect performance or risk differences in behavior.
  • However through a switch, the compiler could be instructed to 'turn on DI'.  When the app is recompiled and run, every use of the new keyword results in a call to the currently-configured DI provider.
  • Microsoft would release updates to legacy .NET framework versions to provide the above capability (requiring recompilation to take advantage of it).

Imagine the advantages of such a change:

  • We would be able to make huge changes to legacy apps, without having to wade through the code, needing only a recompile and some configuration;
  • The syntax for getting objects from a DI container could be consolidated with that for manual/traditional construction based on the new keyword.  This would clean things up compared to all current DI implementations that I know of.
  • The learning curve for new DI frameworks would be drastically reduced, since they would all support the same interface for getting new objects.  Of course their configuration APIs would still differ.
  • We would be able to instantiate interfaces, like so:
    var foo = new IFoo();
  • As I mentioned above, when you use a DI container, you pretty much need a dependency on it across the board - and you will have a very hard time switching to a different DI container in the future.  This would help eliminate that problem, especially if you can stick with new and avoid using the API for obtaining objects.
I imagine someone else has thought of this already, and I'd like to hear from anyone who knows more about the idea.

Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , , , ,

Software

Related posts

Comments are closed

Powered by BlogEngine.NET 1.2.0.0
Theme by Mads Kristensen


Calendar

<<  August 2010  >>
MoTuWeThFrSaSu
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar

Pages

    Recent posts

    Recent comments

    Archive

      Disclaimer

      The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

      © Copyright 2010

      Sign in