Rather than spending time doing the "busy work" of creating and connecting your objects, you just tell Ninject which objects depend on which, and it figures out the rest. This is done most easily through the
"[Inject]" attribute. (There are other more advanced ways to instruct Ninject to inject dependencies, covered later.)
C#:
class Samurai {
private IWeapon _weapon;
[Inject]
public Samurai(IWeapon weapon) {
_weapon = weapon;
}
public void Attack(string target) {
_weapon.Hit(target);
}
}
VB.NET:
Public Class Samurai
Private ReadOnly _sword As IWeapon
<Inject()> _
Public Sub New(ByVal weapon As IWeapon)
_sword = weapon
End Sub
Public Sub Attack(ByVal target As String)
_sword.Hit(target)
End Sub
End Class
When Ninject sees the
"[Inject]" attribute on a type's constructor, it tries to resolve a value for each of the constructor's arguments. But wait a second...
IWeapon is an interface. You can't create an instance of an interface itself, so how does Ninject know what implementation of
IWeapon to inject?
This is accomplished through
type bindings. A type binding (or just binding) is a mapping between a service type (generally an interface or abstract class), and an implementation type. Bindings are typically expressed via Ninject's fluent interface. For example, to arm our
Samurai with a
Sword, we could declare the following binding:
C#:
Bind<IWeapon>().To<Sword>();
VB.NET:
Bind(Of IWeapon).To(Of Sword)()
Or, if you prefer, there's a version without generics, which can be useful for automating:
C#:
Bind(typeof(IWeapon)).To(typeof(Sword));
VB.NET:
Bind(GetType(IWeapon)).To(GetType(Sword))
This means that whenever Ninject encounters a dependency on
IWeapon, it will resolve an instance of
Sword and inject it. This dependency resolution process is a recursive one; that is, if
Sword has any dependencies of its own, they will also be resolved before the
Samurai's constructor is called. (Also, if the
Sword's dependencies have dependencies as well, they will be resolved in turn, and so on.) In this way, Ninject can wire up an entire graph of objects for you, with minimal work on your end. You just need to set up the path, and Ninject will follow it.
This idea of type bindings is common in dependency injectors. However, most of the existing frameworks rely on XML mapping files to set up the bindings between types. Through its fluent interface, Ninject allows you to take advantage of the features of your language (like type-safety) and your IDE (like IntelliSense and code completion).
There are other kinds of bindings too. Some of them are more advanced, and will be covered later. Of note is a
self-binding. You can use the same type as both the service and the implementation. For example, the following line binds the
Samurai type to itself:
C#:
Bind<Samurai>().ToSelf();
VB.NET:
Bind(Of Samurai).ToSelf()
Bear in mind that only concrete types can be self-bound; abstract types and interfaces won't work. Also, if you request an instance of a type that can be self-bound, and there are no bindings defined for the type, Ninject will automatically create an implicit self-binding. It's up to you whether you want to define your bindings explicitly, or let Ninject figure it out.
You can also have multiple bindings for one service type, controlled by conditions that examine the context in which the instance is being resolved. This is the contextual binding system mentioned earlier, and is the key to unlocking the full power of Ninject. Don't worry, it's discussed in excruciating detail later. :-)
Continue reading:
Injection Patterns