If you haven't heard about dependency injection yet, it's programming style where you
write classes in way that they get any objects they access handed to them instead of
going shopping in your object tree. This promotes decoupling, reusability and allows
you to easily isolate classes for unit testing.
Combined with an Inversion of Control container,
you actually have less work organizing a project and wiring your components up to each
other than if you didn't use dependency injection.
To get started, assume I had written an
FpsComponent for displaying my
game's current frames per second with a constructor like this...
...I could now ask an IoC container for an instance of the
and it would determine that to construct an
FpsComponent, it first needs
to construct the component that provides the
it would look up or create a
GraphicsDeviceManager and hand it over to
What this means in practice is that you don't have to pass around global game services
GraphicsDeviceManager) any longer. If you need access to
a service, add it to your constructor and you're done.
And because now all your dependencies are listed neatly in the constructor, it's easy
to write unit tests that mock those services so your components can be
tested in isolation. That's much more test-friendly than letting the
FpsComponent grab the dependencies itself, eg. by handing it your
Game class instance and letting it rummage around for whatever it needs ;-)
For a more in-depth introduction, check out my
Introduction to Dependency Injection!
Dependency Injection and XNA
Wiring up dependency injection in an XNA game is not so easy. The
GraphicsDeviceManager, for example, wants an instance of the
Game class. But the
Game class has to construct
GraphicsDeviceManager in its constructor, so it can register
is called and other components try to access it.
are created by the
Game class, so you would have to tell your
dependency injector to create a
Game and query its
Services property when a component is dependent upon the
What I've done is take Ninject, wire it up as good as I could and then find
a workaround for the above issues. Which was to explicitely tell Ninject about
Game instance in the constructor, before the
is created (which can then happen cleanly through Ninject).
Next, to get the
ContentManager to show up in Ninject, I created two adapters
that depend on the
Game class so it looks to Ninject as if the two
adapters were components requiring the
Game class, whereas
actually, the adapters delegate their work to the
The good thing is that these tweaks are only required because the
class didn't anticipate dependency injection. Once in place, Ninject will work in an XNA game
without further complications. So I can just request any
from Ninject, and it will happily construct it, without the component even knowing that it's
taking part in an elaborate dependency injection scheme =)
Even better is that Ninject works flawlessly on the XBox 360. It's one of the few
dependency injection frameworks that can cope with the Compact Framework and
selecting the right compilation flags for the XBox 360's XNA Runtime was a breeze.
I've put together a small example game that makes use of Ninject to construct a
game component (called
Ninject XNA Demo Project
This is what you'll find in the example project:
Program.cs - Wires up the dependencies and creates the
Game instance using Ninject.
Game class which creates the
GraphicsDeviceManager through dependency injection as well as
FpsComponent - Example component that displays the
current frame rate. Constructed using dependency injection.
Scaffolding/GameModule.cs - Configures the dependencies
(eg. which class provides which services) for Ninject.
Scaffolding/NinjectGame.cs - Custom
class from which you need to derive instead to make Ninject work with XNA.
Scaffolding/ISharedContentService - An example service
that can be pulled in as a dependency by components that want to access
the game's global
Ninject builds for PC and XBox 360 are included in the download, as well as an
XBox 360 solution you can use to test things out on your console!