Because the IContainer also implements IBinder, all the following examples would also work directly with the container. It is just a best practice to work with the most specific interface (for instance, IBinder).

Binding concrete types

This is the best type of binding, because it lets Injexit create the object and resolve all sub-dependencies:

Container.Bind<IFoo, Foo>();
Container.Bind<IGoo, Goo>();
Container.Bind<IBar, Bar>();

Here, all Bar instances will automatically have their constructor injected with instances of Foo and Goo, because the Bar constructor has IFoo and IGoo parameters, which are respectively bound to the Foo and Goo concrete classes.

Binding instances

In cases where you already have an instance and simply want to inject it into other objects, use this form:

Foo fooInstance = ...
Container.BindInstance<IFoo>(fooInstance);

Or if your instance is already downcasted to the proper interface type, you may omit that type for brevity:

IFoo fooInstance = ...
Container.BindInstance(fooInstance);

Warning: If you don’t specify the generic type, as in the example above, make sure that the variable is exactly of the type that needs to be injected.

Instance binding is useful when you need to inject a specific game object or component into another object. Just expose a field on you script (that you may assign via the Inspector) and bind that field with BindInstance():

using Silphid.Injexit;
using UnityEngine;

public class AppInstaller : RootInstaller
{
  // Assign a value to this field via the Inspector
  public MyComponent myComponent;

  protected override void Configure(IBinder binder)
  {
    Container.BindInstance(myComponent);
    
    // Equivalent to:
    // Container.BindInstance<MyComponent>(myComponent);
  }
}

For anything else than MonoBehaviours, the downside of binding a specific instance is that the instance will not have its own dependencies injected, because Injexit did not instantiate it. For MonoBehaviours part of your scene, that’s OK, because they get injected automatically anyway by the Installer.

Binding shared objects

By default, Injexit will create a new instance of a class for every other class that depends on it. This is a good pattern, because it limits side-effects between objects. However, sometimes you want an object to be shared, so that changes made to it will be visible to all other objects sharing it as a dependency. This is achieved using the .AsSingle() suffix:

Container.Bind<IFoo, Foo>().AsSingle();

Note that instance binding using BindInstance() does not support the .AsSingle() suffix, because instances specified explicitly are forcibly always shared.

Binding lists of objects

Sometimes, you want many objects implementing the same interface to be injected together as a List<T>, IEnumerable<T> or T[]:

public class Foo1 : IFoo {}
public class Foo2 : IFoo {}
public class Foo3 : IFoo {}

public class Bar : IBar
{
  public Bar(List<IFoo> foos)
  {
  }
}

In that case, you would bind them with the .AsList() suffix:

var foo3 = new Foo3();

Container.Bind<IFoo, Foo1>().AsList();
Container.Bind<IFoo, Foo2>().AsList();
Container.BindInstance<IFoo>(foo3).AsList();

Here, the Bar constructor will be injected with a List<IFoo> containing instances of the Foo1, Foo2 and Foo3 classes.

Note that Injexit will automatically combine the injected objects into a List<T>, an IEnumerable<T> or a T[], depending on the type that is expected at the injection point.

A more convenient syntax to avoid repetition, which is equivalent to the example above, is:

Container.BindAsListOf<IFoo>(x =>
{
    x.Bind<Foo1>();
    x.Bind<Foo2>();
    x.BindInstance(foo3);
});

Binding all implementations/derivatives as a list

Container.BindAllAsListOf<IFoo>();

This will scan the assembly where IFoo is defined for all classes implementing that interface (for instance, Foo1, Foo2 and Foo3) and bind them as a list of IFoo.

You may also specify the assembly to scan explicitly:

Container.BindAllAsListOf<IFoo>(GetType().Assembly);

Qualifying bindings with identifiers

When you need finer control over what gets injected where, for instance when you need different implementations of an interface to be injected in various places, you will have to tag your bindings with identifiers:

Container.Bind<IFoo, Foo1>().WithId("Fire");
Container.Bind<IFoo, Foo2>().WithId("Rain");
Container.BindInstance<IFoo>(foo3).WithId("Ice");

And then also tag your injection points with the same identifiers using [Id] attributes:

public class Bar : IBar
{
  // Field injection
  [Inject]
  [Id("Fire")]
  public IFoo Fire;

  // Constructor injection
  public Bar([Id("Rain")] IFoo rain)
  {  
  }
  
  // Method injection
  [Inject]
  public void Init([Id("Ice")] IFoo ice)
  {
  }
}

Identifiers are also very helpful for lists:

Container.BindAsList<IFoo>(x =>
  {
    x.Bind<Foo1>();
    x.Bind<Foo1>();
    x.Bind<Foo2>();
  })
  .WithId("List1");

Container.BindAsList<IFoo>(x =>
  {
    x.Bind<Foo2>();
    x.Bind<Foo3>();
  })
  .WithId("List2");

// You can even mix the two syntaxes.
// This will be added to "List2"
Container.Bind<IFoo, Foo3>().AsList().WithId("List2");

Finally, simply inject those two lists like so:

public class Bar : IBar
{
  public Bar(
      [Id("List1")] List<IFoo> list1,
      [Id("List2")] List<IFoo> list2)
  {
  }
}

Optional injection

When injection is not required, you may tag your injection points with the [Optional] attribute:

public class Bar : IBar
{
  // Field injection
  [Inject]
  [Optional]
  public IFoo Foo;

  // Constructor injection
  public Bar([Optional] IFoo foo)
  {
  }
  
  // Method injection
  [Inject]
  public void Init([Optional] IFoo foo)
  {
  }
}

The [Optional] attribute is not necessary for parameters with default values.

TODO: Example with optional int parameters...

Controlling the composition tree explicitly

I am a big fan of the Composite and Decorator design patterns and I use them abundantly, notably in Loadzup. The problem they raise with dependency injection is that they require precise control over their composition tree. You cannot just register each class individually and expect the container to figure how you want all of them to be assembled together. Let’s take an example from Loadzup, where the ILoader interface is implemented by HttpLoader, ResourceLoader and CompositeLoader. The responsibility of CompositeLoader is to delegate to the proper child loader according to the URI scheme, so its constructor expects a list of ILoader child objects to delegate to:

public class CompositeLoader : ILoader
{
  // ...
  
  public CompositeLoader(params ILoader[] children)
  {
    // ...
  }

  // ...
}

In order to tell the container specifically how to assemble those three classes, we can use the Using() method:

Container.Bind<ILoader, CompositeLoader>().AsSingle().Using(x =>
{
    x.Bind<ILoader, HttpLoader>().AsList();
    x.Bind<ILoader, ResourceLoader>().AsList();
});

In this case, the HttpLoader and ResourceLoader classes are only exposed to the CompositeLoader and when the rest of the application will require an ILoader, it will be injected a CompositeLoader.

I love this syntax because it shows the composition tree in a very visual way.

However, in order to maximize the benefits of dependency injection, only use this syntax when you need explicit control over the composition tree. Otherwise, you better let the container figure the composition tree for you.

Forwarding multiple interfaces to same binding

Sometimes the same class implements multiple interfaces and you want all those interfaces to be bound to the same object, with the same binding options.

public class FooGooBar : IFoo, IGoo, IBar {}

Simply use the Alias() operator to specify those extra interfaces:

Container.Bind<IFoo, FooGooBar>().Alias<IGoo>().Alias<IBar>().AsSingle();

Here, the IFoo, IGoo and IBar interfaces will all be bound to the same FooGooBar instance.

Self-binding a type

Even though it is a best practice to access most of your concrete classes through abstract interfaces, especially in the context of dependency injection, in some cases you might only have a concrete type with no interface and prefer to inject it as is.

Container.BindToSelf<Foo>();
Container.BindToSelf<Goo>();

Showzup uses that approach for models and view models, which do not have each their own interface.

This approach however defeats multiple advantages of dependency injection, such as being able to swap an implementation of an interface with some other arbitrary implementation, so try to avoid it, unless it’s part of your design.

Self-binding all of a type’s derivatives

Container.BindToSelfAll<IFoo>();

This will scan the assembly where IFoo is defined for all classes implementing that interface (for instance, Foo1, Foo2 and Foo3) and self-bind them. In this case, the IFoo interface serves only as a marker. This approach is used in Showzup to automatically bind all view model classes.

You may also specify the assembly to scan explicitly:

Container.BindToSelfAll<IFoo>(GetType().Assembly);

Self-binding multiple instances

If you want multiple instances of different types, that will only be known at run-time, to be bound to their own type, you can use the BindInstances() extension method:

IFoo foo = new Foo();
IGoo goo = new Goo();
Container.BindInstances(foo, goo);

Or:

var objects = new object[] { new Foo(), new Goo() };
Container.BindInstances(objects);

This is equivalent to:

Container.BindInstance<Foo>(foo);
Container.BindInstance<Goo>(goo);

Note that, even if foo and goo are passed as interfaces IFoo and IGoo or as an array of objects , the BindInstances() method always binds each object to its own concrete type (ie: Foo or Goo). That means that the dependent object must also declare its dependencies using those concrete types.

Tags: