Skip to content

Latest commit

 

History

History
executable file
·
1977 lines (1432 loc) · 74.7 KB

README.md

File metadata and controls

executable file
·
1977 lines (1432 loc) · 74.7 KB

Adic

Another Dependency Injection Container for Unity and beyond

Unity Asset Store Donate

Contents

  1. Introduction
  2. Features
  3. Concepts
    1. About dependency injection (DI)
      1. What is a DI container?
      2. Why use a DI container?
      3. Why use it with Unity?
      4. Common use cases
      5. Further readings
    2. Structure
    3. Types of bindings
    4. Namespace conventions
    5. Chaining
  4. Quick start
  5. API
    1. Bindings
    2. Constructor injection
    3. Member injection
    4. Method injection
    5. Multiple constructors
    6. Multiple injection
    7. Behaviour injection
      1. MonoBehaviour injection
      2. StateMachineBehaviour injection
      3. Scene injection
      4. Injecting from multiple containers
    8. Conditions
    9. Update
    10. Dispose
    11. Instance resolution modes
    12. Manual type resolution
    13. Factories
    14. Bindings setup
    15. Using commands
  6. Multiple scenes
  7. Order of events
  8. Script execution order
  9. Performance
  10. IL2CPP, AOT and code stripping
  11. General notes
  12. Extensions
    1. Available extensions
      1. Bindings Printer
      2. Bindings Setup
      3. Commander
      4. Context Root
      5. Event Caller
      6. Mono Injection
      7. State Injection
      8. Unity Binding
    2. Creating extensions
    3. Container events
      1. Binder events
      2. Injector events
  13. Binaries
  14. Examples
  15. Changelog
  16. Support
  17. License

Introduction

Adic is a lightweight dependency injection container for Unity and any C# (or .Net) project.

Based on the proof of concept container from Sebastiano Mandalà and studies of StrangeIoC, the intention of the project is to create a dependency injection container that is simple to use and extend, having on its roots the simplicity of the work of Mandalà and the extensibility of StrangeIoC, also borrowing some ideas from the classic Unity Application Block.

The project is compatible with Unity 5 and 4. Tested on Windows/Mac/Linux, Android, iOS, WP10 (IL2CPP), Web Player and WebGL.

Also available on the Unity Asset Store.

Features

  • Bind types, singleton instances, factories, game objects and prefabs.
  • Instance resolution by type, identifier and complex conditions.
  • Injection on constructor, fields and properties.
  • Can inject multiple objects of the same type.
  • Can resolve and inject instances from types that are not bound to the container.
  • Can inject automatically on components of a scene.
  • Fast dependency resolution with internal cache.*
  • Use of attributes to indicate injections, preferable constructors and post constructors.
  • Can be easily extended through extensions.
  • Framework decoupled from Unity - all Unity based API is achieved through extensions.
  • Organized and well documented code written in C#.

Concepts

About dependency injection (DI)

What is a DI container?

A dependency injection container is a piece of software that handles the resolution of dependencies in objects. It's related to the dependency injection and inversion of control design patterns.

The idea is that any dependency an object may need should be resolved by an external entity rather than the own object. Practically speaking, an object should not use new to create the objects it uses, having those instances injected into it by another object whose sole existence is to resolve dependencies.

So, a dependency injection container holds information about dependencies (the bindings) that can be injected into another objects by demand (injecting into existing objects) or during resolution (when you are creating a new object of some type).

Why use a DI container?

In a nutshell, to decouple your code.

A DI container, in pair with a good architecture, can ensure SOLID principles and help you write better code.

Using such container, you can easily work with abstractions without having to worry about the specifics of each external implementation, focusing just on the code you are writing. It's all related to dependencies: any dependency your code needs is not resolved directly by your code, but externally, allowing your code to deal only with its responsibilities.

As a plus, there are other benefits from using a DI container:

  1. Refactorability: with your code decoupled, it's easy to refactor it without affecting the entire codebase.
  2. Reusability: thinking about abstractions allows your code to be even more reusable by making it small and focused on a single responsibility.
  3. Easily change implementations: given all dependencies are configured in the container, it's easy to change a implementation for a given abstraction. It helps e.g. when implementing generic functionality in a platform specific way.
  4. Testability: by focusing on abstractions and dependency injection, it's easy to replace implementations with mock objects to test your code.
  5. Improved architecture: your codebase will be naturally better and more organized because you'll think about the relationships of your code.
  6. Staying sane: by focusing on small parts of the code and having a consistent architecture, the sanity of the developer is also ensured!

Why use it with Unity?

Unity is not SOLID friendly out of the box. Even the official examples may give a wrong idea on how to code on Unity. Using a DI container in conjunction with Unity, it's possible to write code that is more extensible, reusable and less MonoBehaviour centric (in most cases, a regular class can do just fine or better).

This way your code can become more modular and your components less tightly coupled to each other.

Common use cases

Class dependency

Imagine you class depend on a given service that provides some action it may need:

public class MyClass {
	public void DoAction() {
		var service = new SomeService();
		service.SomeAction();
	}
}

If in the future you need to change the implementation of the service, you'll have to get back to the class and change it. It can work just fine for small projects, but as the codebase grows, it can become a (error prone) nightmare to chase all these references.

So, you can change to a more decoupled code, making MyClass not having to worry about the specific implementation of SomeService it uses:

public class MyClass {
	private IService service;

	public MyClass(IService service) {
		this.service = service;
	}

	public void DoAction() {
		this.service.SomeAction();
	}
}

The idea is that you invert the resolution of the dependency up into the execution flow.

Now, any class that needs to use MyClass also has to to provide a service reference to it by constructor:

public class MyOtherClass {
	private IService service;

	public MyOtherClass(IService service) {
		this.service = service;
	}

	public void DoAction() {
		var myClass = new MyClass(this.service)
		myClass.DoAction();
	}
}

But you could write it even better: given MyOtherClass depends only on MyClass (IService is just a tramp variable - a variable that is there to be passed to other object), there's no need to store a reference to the IService object:

public class MyOtherClass {
	private MyClass myClass;

	public MyOtherClass(MyClass myClass) {
		this.myClass = myClass;
	}

	public void DoAction() {
		this.myClass.DoAction();
	}
}

However, any class that uses MyOtherClass must also fullfill any dependencies it needs, again up into the execution flow, until a place where all the dependencies are resolved. This place is called the composition root.

And that's where a DI container come in handy. In the composition root, a DI container is created and configured to resolve and wire all dependencies of any objects used by your code so you don't have to worry about it!

Further readings

Structure

The structure of Adic is divided into five parts:

  1. InjectionContainer/Container: binds, resolves, injects and holds dependencies. Technically, the container is a Binder and an Injector at the same time.
  2. Binder: binds a type to another type or instance with inject conditions.
  3. Injector: resolves and injects dependencies.
  4. Context Root: main context in which the containers are in. Acts as an entry point for the game. It's implemented through an extension.
  5. Extensions: provides additional features to the framework.

Types of bindings

  • Transient: a new instance is created each time a dependency needs to be resolved.
  • Singleton: a single instance is created and used on any dependency resolution.
  • Factory: creates the instance and returns it to the container.

Namespace conventions

Adic is organized internally into different namespaces that represents the framework components. However, the commonly used components are under Adic namespace:

  1. Inject attribute;
  2. InjectionContainer;
  3. IFactory;
  4. Extensions (like ContextRoot and UnityBinding).

Chaining

Methods from the container and bindings creation can be chained to achieve a more compact code:

//Create the container.
this.AddContainer<InjectionContainer>()
	//Register any extensions the container may use.
	.RegisterExtension<CommanderContainerExtension>()
	.RegisterExtension<EventCallerContainerExtension>()
	.RegisterExtension<UnityBindingContainerExtension>()
	//Add bindings.
    .Bind<Type1>.To<AnotherType1>()
    .Bind<Type2>.To<AnotherType2>().As("Identifier")
    .Bind<Type3>.ToGameObject("GameObjectName").AsObjectName()
    .Bind<Type4>.ToSingleton<AnotherType3>();

Good practice: when chaining, always place the bindings in the end of the chain or use bindings setup to organize your bindings.

Quick start

1. Create the context root (e.g. GameRoot.cs) of your scene by inheriting from Adic.ContextRoot.

using UnityEngine;

namespace MyNamespace {
	/// <summary>
	/// Game context root.
	/// </summary>
	public class GameRoot : Adic.ContextRoot {
		public override void SetupContainers() {
			//Setup the containers.
		}

		public override void Init() {
			//Init the game.
		}
	}
}

Note: there should be only one context root per scene.

Hint: when using a context root for each scene of your game, to make the project more organized, create folders for each of your scenes that will hold their own scripts and context roots.

2. In the SetupContainers() method, create and add any containers you may need, also configuring their bindings.

public override void SetupContainers() {
	//Create a container.
	this.AddContainer<InjectionContainer>()
		//Setup bindinds.
		.Bind<Whatever>().ToSelf();
}

Attention: the order in which you place the bindings is very important. If class A requires an injection of class B, class B should be bound to the container before class A.

Hint: in Adic, the lifetime of your bindings is the lifetime of your containers - you can create as many containers as you want to hold your dependencies.

Good practice: if you have many bindings to add to a container, it's better to create reusable objects that can setup related bindings together. Please see Bindings setup for more information.

3. On the Init() method, place any code to start your game.

Note: the idea of this method is to work as an entry point for your game, like a main() method on console applications.

4. Attach the context root created by you on an empty game object in your scene.

5. Start dependency injecting!

API

Bindings

Binding is the action of linking a type to another type or instance. Adic makes it simple by providing different ways to create your bindings.

Every binding must occur from a certain key type by calling the Bind() method of the container.

The simple way to bind e.g. some interface to its class implementation is as below:

container.Bind<SomeInterface>().To<ClassImplementation>();

It's also possible to bind a class to an existing instance:

container.Bind<SomeInterface>().To(someInstance);

You can also bind a Unity component to a game object that has that particular component:

container.Bind<Transform>().ToGameObject("GameObjectNameOnHierarchy");

Or a prefab on some Prefabs/Whatever resources folder:

container.Bind<Transform>().ToPrefab("Prefabs/Whatever/MyPrefab");

And, if needed, non-generics versions of bindings' methods are also available:

container.Bind(someType).To(anotherType);

The next sections will cover all the available bindings Adic provides.

To Self

Binds the key type to a transient of itself. The key must be a class.

container.Bind<ClassType>().ToSelf();

To Singleton

Binds the key type to a singleton of itself. The key must be a class.

container.Bind<ClassType>().ToSingleton();

It's also possible to create a singleton of the key type to another type. In this case, the key may not be a class.

//Using generics...
container.Bind<InterfaceType>().ToSingleton<ClassType>();
//...or instance type.
container.Bind<InterfaceType>().ToSingleton(classTypeObject);

To another type

Binds the key type to a transient of another type. In this case, the To type will be instantiated every time a resolution of the key type is asked.

//Using generics...
container.Bind<InterfaceType>().To<ClassType>();
//..or instance type.
container.Bind<InterfaceType>().To(classTypeObject);

To instance

Binds the key type to an instance.

//Using generics...
container.Bind<InterfaceType>().To<ClassType>(instanceOfClassType);
//..or instance type.
container.Bind<InterfaceType>().To(classTypeObject, instanceOfClassType);

To all types in a namespace as transient

Binds the key type to all assignable types in a given namespace as transient bindings.

Note 1: it will create a multiple binding if there's more than one type in the namespace that is assignable to the key type.

Note 2: currently it's not possible to use conditions when binding to all types in a namespace.

container.Bind<SomeType>().ToNamespace("MyNamespace.Whatever");

To all types in a namespace as singleton

Binds the key type to all assignable types in a given namespace as singleton bindings.

Note 1: it will create a multiple binding if there's more than one type in the namespace that is assignable to the key type.

Note 2: currently it's not possible to use conditions when binding to all types in a namespace.

container.Bind<SomeType>().ToNamespaceSingleton("MyNamespace.Whatever");

To a Factory

Binds the key type to a factory. The factory must implement the Adic.IFactory interface.

//Binding factory by generics...
container.Bind<InterfaceType>().ToFactory<Factory>();
//...or type instance...
container.Bind<InterfaceType>().ToFactory(typeFactory);
//...or a factory instance.
container.Bind<InterfaceType>().ToFactory(factoryInstance);

See Factories for more information.

To game object

Binds the key type to a singleton of itself or some type on a new game object.

Good practice: to prevent references to destroyed objects, only bind to game objects that won't be destroyed in the scene.

//Binding to itself...
container.Bind<SomeMonoBehaviour>().ToGameObject();
//...or some other component using generics...
container.Bind<SomeInterface>().ToGameObject<SomeMonoBehaviour>();
//..or some other component by instance type.
container.Bind<SomeInterface>().ToGameObject(someMonoBehaviourType);

The newly created game object will have the same name as the key type.

To game object by name

Binds the key type to a singleton UnityEngine.Component of itself or some type on a game object of a given name.

Good practice: to prevent references to destroyed objects, only bind to game objects that won't be destroyed in the scene.

If the component is not found on the game object, it will be added.

//Binding to itself by name...
container.Bind<SomeMonoBehaviour>().ToGameObject("GameObjectName");
//...or some other component using generics and name...
container.Bind<SomeInterface>().ToGameObject<SomeMonoBehaviour>("GameObjectName");
//..or some other component by instance type and name.
container.Bind<SomeInterface>()().ToGameObject(someMonoBehaviourType, "GameObjectName");

To game object with tag

Binds the key type to a singleton UnityEngine.Component of itself or some type on a game object of a given tag.

Good practice: to prevent references to destroyed objects, only bind to game objects that won't be destroyed in the scene.

If the component is not found on the game object, it will be added.

//Binding to itself by tag...
container.Bind<SomeMonoBehaviour>().ToGameObjectWithTag("Tag");
//...or some other component using generics and tag...
container.Bind<SomeInterface>().ToGameObjectWithTag<SomeMonoBehaviour>("Tag");
//..or some other component by instance type and tag.
container.Bind<SomeInterface>().ToGameObjectWithTag(someMonoBehaviourType, "Tag");

To game objects with tag

Binds the key type to singletons UnityEngine.Component of itself or some type on a game object of a given tag.

Good practice: to prevent references to destroyed objects, only bind to game objects that won't be destroyed in the scene.

If the component is not found on the game object, it will be added.

//Binding to itself by tag...
container.Bind<SomeMonoBehaviour>().ToGameObjectsWithTag("Tag");
//...or some other component using generics and tag...
container.Bind<SomeInterface>().ToGameObjectsWithTag<SomeMonoBehaviour>("Tag");
//..or some other component by instance type and tag.
container.Bind<SomeInterface>().ToGameObjectsWithTag(someMonoBehaviourType, "Tag");

To prefab transient

Binds the key type to a transient UnityEngine.Component of itself or some type on the prefab.

If the component is not found on the game object, it will be added.

Note: every resolution of a transient prefab will generate a new instance. So, even if the component resolved from the prefab is destroyed, it won't generate any missing references in the container.

//Binding prefab to itself...
container.Bind<SomeMonoBehaviour>().ToPrefab("Prefabs/Whatever/MyPrefab");
//...or to another component on the prefab using generics...
container.Bind<SomeInterface>().ToPrefab<SomeMonoBehaviour>("Prefabs/Whatever/MyPrefab");
//...or to another component on the prefab using instance tyoe.
container.Bind<SomeInterface>().ToPrefab(someMonoBehaviourType, "Tag");

To prefab singleton

Binds the key type to a singleton UnityEngine.Component of itself or some type on a newly instantiated prefab.

Good practice: to prevent references to destroyed objects, only bind to prefabs that won't be destroyed in the scene.

//Binding singleton prefab to itself...
container.Bind<SomeMonoBehaviour>().ToPrefabSingleton("Prefabs/Whatever/MyPrefab");
//...or to another component on the prefab using generics...
container.Bind<SomeInterface>().ToPrefabSingleton<SomeMonoBehaviour>("Prefabs/Whatever/MyPrefab");
//...or to another component on the prefab using instance type.
container.Bind<SomeInterface>().ToPrefabSingleton(someMonoBehaviourType, "Tag");

To resource

Binds the key type to a singleton UnityEngine.Object loaded from the Resources folder.

Good practice: To prevent references to destroyed objects, only bind to resources that won't be destroyed in the scene.

container.Bind<AudioClip>().ToResource("Audio/MyAudio");

Constructor injection

Adic will always try to resolve any dependencies the constructor may need by using information from its bindings or trying to instantiate any types that are unknown to the binder.

There's no need to decorate constructors' parameteres with Inject attributes - they will be resolved automatically. However, if you are using identified parameters, you should use the Inject attribute:

namespace MyNamespace {
	/// <summary>
	/// My class summary.
	/// </summary>
	public class MyClass {
		/// <summary>
		/// Class constructor.
		/// </summary>
		/// <param name="param1">Identified parameter description.</param>
		/// <param name="param2">Parameter description.</param>
		public MyClass([Inject("Identifier")] param1, SomeType param2) {
			...
		}
	}
}

Note: if there's more than one constructor, Adic always look for the one with less parameteres. However, it's possible to indicate which constructor should be used on a multi constructor class.

Member injection

Adic can perform dependency injection on public fields and properties of classes. To make it happen, just decorate the members with the Inject attribute:

namespace MyNamespace {
	/// <summary>
	/// My class summary.
	/// </summary>
	public class MyClass {
		/// <summary>Field to be injected.</summary>
		[Inject]
		public SomeClass fieldToInject;
		/// <summary>Field NOT to be injected.</summary>
		public SomeClass fieldNotToInject;

		/// <summary>Property to be injected.</summary>
		[Inject]
		public SomeOtherClass propertyToInject { get; set; }
		/// <summary>Property NOT to be injected.</summary>
		public SomeOtherClass propertyNotToInject { get; set; }
	}
}

Method injection

Method injection works like constructor injection, but on methods:

namespace MyNamespace {
	/// <summary>
	/// My class summary.
	/// </summary>
	public class MyClass {
		/// <summary>
		/// Injected method, called after all the dependencies have been resolved.
		/// </summary>
		/// <param name="param1">Identified parameter description.</param>
		/// <param name="param2">Parameter description.</param>
		[Inject]
		public void MyMethod1([Inject("Identifier")] SomeType param1, SomeType param2) {
			...
		}

		/// <summary>
		/// Injected method, called after all the dependencies have been resolved.
		/// </summary>
		/// <param name="param">Parameter description.</param>
		[Inject]
		public void MyMethod2(SomeType param) {
			...
		}
	}
}

Method injection occurs after all injections on the class. So, it's possible to use it as a post constructor:

namespace MyNamespace {
	/// <summary>
	/// My class summary.
	/// </summary>
	public class MyClass {
		/// <summary>Field to be injected.</summary>
		[Inject]
		public SomeClass fieldToInject;

		/// <summary>
		/// Class constructor.
		/// </summary>
		public MyClass() {
			...
		}

		/// <summary>
		/// Injected method acting as a post constructor.
		/// </summary>
		[Inject]
		public void PostConstructor() {
			...
		}
	}
}

Good practice: injected methods can be used as constructors on MonoBehaviour components.

Good practice: injected methods can be used to perform initilizations or configurations on objects.

Good practice: always use non-generic injected methods to prevent JIT compile method exceptions on AOT platforms (like iOS and WebGL).

Multiple constructors

In case you have multiple constructors, it's possible to indicate to Adic which one should be used by decorating it with the Inject attribute:

namespace MyNamespace {
	/// <summary>
	/// My class summary.
	/// </summary>
	public class MyClass {
		/// <summary>
		/// Class constructor.
		/// </summary>
		public MyClass() {
			...
		}

		/// <summary>
		/// Class constructor.
		/// </summary>
		/// <param name="parameterName">Parameter description</param>
		[Inject]
		public MyClass(Type parameterName) {
			...
		}
	}
}

Multiple injection

It's possible to inject multiple objects of the same type by creating a series of bindings of the same key type. In this case, the injection occurs on an array of the key type.

Binding multiple objects to the same key:

container
	.Bind<GameObject>().ToGameObject("Enemy1")
	.Bind<GameObject>().ToGameObject("Enemy2")
	.Bind<GameObject>().ToGameObject("Enemy3")
	.Bind<GameObject>().ToGameObject("Enemy4")
	.Bind<GameObject>().ToGameObject("Enemy5");

Multiple injection in a field:

namespace MyNamespace {
	/// <summary>
	/// My class summary.
	/// </summary>
	public class MyClass {
		/// <summary>Enemies on the game.</summary>
		[Inject]
		public GameObject[] enemies;
	}
}

It's possible to manually resolve multiple objects. Please see Manual type resolution for more information.

Behaviour injection

It's possible to perform injection on custom MonoBehaviour and StateMachineBehaviour scripts through the extensions Mono Injection and State Injection, which are enabled by default.

MonoBehaviour injection

To perform injection on custom MonoBehaviour fields and properties, simply call the Inject() extension method of the MonoBehaviour:

using Unity.Engine;

namespace MyNamespace {
	/// <summary>
	/// My MonoBehaviour summary.
	/// </summary>
	public class MyBehaviour : MonoBehaviour {
		/// <summary>Field to be injected.</summary>
		[Inject]
		public SomeClass fieldToInject;

		protected void Start() {
			this.Inject();
		}
	}
}
Base MonoBehaviour

To make injection even simpler, create a base behaviour from which all your MonoBehaviour can inherit, calling the Inject() method during Start():

using Unity.Engine;

namespace MyNamespace {
	/// <summary>
	/// Base MonoBehaviour.
	/// </summary>
	public abstract class BaseBehaviour : MonoBehaviour {
		/// <summary>
		/// Called when the component is starting.
		///
		/// If overriden on derived classes, always call base.Start().
		/// </summary>
		protected virtual void Start() {
			this.Inject();
		}
	}
}

StateMachineBehaviour injection

To perform injection on custom StateMachineBehaviour fields and properties, simply call the Inject() extension method on any of the state events:

using Unity.Engine;

namespace MyNamespace {
	/// <summary>
	/// My StateMachineBehaviour summary.
	/// </summary>
	public class MyStateMachineBehaviour : StateMachineBehaviour {
		/// <summary>Field to be injected.</summary>
		[Inject]
		public SomeClass fieldToInject;

		public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
			this.Inject();
		}
	}
}

Note: only available on Unity 5+.

Base StateMachineBehaviour

To make injection even simpler, create a base behaviour from which all your StateMachineBehaviour can inherit:

using Unity.Engine;

namespace MyNamespace {
	/// <summary>
	/// Base StateMachineBehaviour.
	/// </summary>
	public abstract class BaseStateBehaviour : StateMachineBehaviour {
		/// <summary>
		/// Behaviour constructor.
		/// </summary>
		public BaseStateBehaviour() {
			this.Inject();
		}
	}
}

Scene injection

On some performance sensitive games it's important to ensure that every injection occurs before the game starts, in a scene level. Adic provides three ways to perform a scene wide injection, which are configured by selecting the appropriate injection type on the Context Root inspector.

Note: there's no better or worse strategy. It only depends on the game you are working on and developer preferences, given all strategies can achieve the same performance goals if all objects used during the game are created before the game starts. Read the Performance section for more performance considerations.

Manual

The injection is performed manually, without a scene wide automatic injection. This is the default setting.

It's recommended to use a base MonoBehaviour to perform injections.

Children

The injection is performed on all MonoBehaviour or any MonoBehaviour derived type (e.g. a base MonoBehaviour) that are children of the context root.

Base type

The injection is performed on all MonoBehaviour or any MonoBehaviour derived type (e.g. a base MonoBehaviour) throughout the scene.

Injecting from multiple containers

When injecting into MonoBehaviour/StateMachineBehaviour using the this.Inject() method, every available container in the context root is used. If you want to restrict the containers from which injection occurs, use the InjectFromContainer attribute in conjunction with a container identifier.

Setting a container identifier

When creating the container, set an identifier through its constructor:

//Create the container with an identifier.
this.AddContainer(new InjectionContainer("identifier"))
	//Register any extensions the container may use.
	.RegisterExtension<UnityBindingContainerExtension>();

Good practice: identifiers can be any object. However, it's recommended to use only strings and enums.

Adding the attribute

In the MonoBehaviour/StateMachineBehaviour that should receive injection only from a certain container, add the InjectFromContainer attribute with the container's identifier:

MonoBehaviour
using Unity.Engine;

namespace MyNamespace {
	/// <summary>
	/// My MonoBehaviour summary.
	/// </summary>
	[InjectFromContainer("identifier")]
	public class MyBehaviour : MonoBehaviour {
		/// <summary>Field to be injected.</summary>
		[Inject]
		public SomeClass fieldToInject;

		protected void Start() {
			this.Inject();
		}
	}
}
StateMachineBehaviour
using Unity.Engine;

namespace MyNamespace {
	/// <summary>
	/// My StateMachineBehaviour summary.
	/// </summary>
	[InjectFromContainer("identifier")]
	public class MyStateMachineBehaviour : StateMachineBehaviour {
		/// <summary>Field to be injected.</summary>
		[Inject]
		public SomeClass fieldToInject;

		public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
			this.Inject();
		}
	}
}

Conditions

Conditions allow a more customized approach when injecting dependencies into constructors and fields/properties.

Using conditions you can:

1. Tag a binding with an identifier, so you can indicate it as a parameter in the Inject attribute on constructors and fields/properties:

When binding:

container.Bind<SomeInterface>().To<SomeClass>().As("Identifier");

When binding to Unity Objects, it's possible to use the object name automatically as the binding:

container.Bind<SomeInterface>().ToGameObject("GameObjectName").AsObjectName();

When injecting into constructor parameters:

namespace MyNamespace {
	/// <summary>
	/// My class summary.
	/// </summary>
	public class MyClass {
		/// <summary>
		/// Class constructor.
		/// </summary>
		/// <param name="parameter">Parameter to be injected.</param>
		public MyClass([Inject("Identifier")] SomeInterface parameter) {
			//Code.
		}
	}
}

When injecting into fields/properties:

namespace MyNamespace {
	/// <summary>
	/// My class summary.
	/// </summary>
	public class MyClass {
		/// <summary>Field to be injected.</summary>
		[Inject("Identifier")]
		public SomeInterface field;

		/// <summary>Property to be injected.</summary>
		[Inject("Identifier")]
		public SomeInterface property { get; set; }
	}
}

Good practice: identifiers can be any object. However, it's recommended to use only strings and enums.

2. Indicate in which objects a binding can be injected, by type or instance:

//Using generics...
container.Bind<SomeInterface>().To<SomeClass>().WhenInto<MyClass>();
//...or type instance...
container.Bind<SomeInterface>().To<SomeClass>().WhenInto(myClassInstanceType);
//...or by a given instance.
container.Bind<SomeInterface>().To<SomeClass>().WhenIntoInstance(myClassInstanceType);

3. Create complex conditions by using an anonymous method:

container.Bind<SomeInterface>().To<SomeClass>().When(context =>
		context.member.Equals(InjectionMember.Field) &&
        context.parentType.Equals(typeof(MyClass))
	);

The context provides the following fields:

  1. member (Adic.InjectionMember enum): the class member in which the injection is occuring (None, Constructor, Field or Property).
  2. memberType (System.Type): the type of the member in which the injection is occuring.
  3. identifier (object): the identifier of the member in which the injection is occuring (from Inject attribute).
  4. parentType (System.Type): the type of the object in which the injection is occuring.
  5. parentInstance (object): the instance of the object in which the injection is occuring.
  6. injectType (System.Type): the type of the object being injected.

Update

It's possible to have an Update() method on regular classes (that don't inherit from MonoBehaviour) by implementing the Adic.IUpdatable interface.

See Event Caller for more information.

Dispose

When a scene is destroyed, it's possible to have a method that can be called to e.g. free up resources. To do it, implement the System.IDisposable interface on any class that you want to have this option.

See Event Caller for more information.

Instance resolution modes

Adic provides two instance resolution modes:

  1. ALWAYS_RESOLVE (default): always try to resolve every type that requires injection, even ones that are not bound to the container.
  2. RETURN_NULL: only resolves types that are bound to the container. Trying to resolve a non-bound type will return a null reference.

Setting a resolution mode

Instance resolution modes can be configured through the Adic.InjectionContainer constructor or by changing the resolutionMode property:

//Setting a resolution mode through constructor...
var container = new InjectionContainer(ResolutionMode.RETURN_NULL);
//...and changing it through property.
container.resolutionMode = ResolutionMode.ALWAYS_RESOLVE;

Manual type resolution

If you need to get a type from the container but do not want to use injection through constructor or fields/properties, it's possible to execute a manual resolution directly by calling the Resolve() method:

//Resolving using generics...
var instance = container.Resolve<Type>();
//...or using generics for objects with a given identifier...
var instance = container.Resolve<Type>("Identifier");
//...or by type instance...
instance = container.Resolve(typeInstance);
//...or by objects with a given identifier...
instance = container.Resolve("Identifier");
//...or by type instance for objects with a given identifier.
instance = container.Resolve(typeInstance, "Identifier");

It's also possible to resolve all objects of a given type:

//Resolving all objects using generics...
var instances = container.ResolveAll<Type>();
//...or using generics for objects with a given identifier...
var instance = container.ResolveAll<Type>("Identifier");
//...or by type instance...
instances = container.ResolveAll(typeInstance);
//...or by objects with a given identifier...
instance = container.ResolveAll("Identifier");
//...or by type instance for objects with a given identifier.
instance = container.ResolveAll(typeInstance, "Identifier");

Note: although it's possible to resolve instances by identifier, currently manual resolution of bindings that have other conditions is not supported.

Factories

When you need to handle the instantiation of an object manually, it's possible to create a factory class by inheriting from Adic.IFactory:

using Adic.Injection;

namespace MyNamespace {
	/// <summary>
	/// My factory.
	/// </summary>
	public class MyFactory : Adic.IFactory {
		/// <summary>
		/// Creates an instance of the object of the type created by the factory.
		/// </summary>
		/// <param name="context">Injection context.</param>
		/// <returns>The instance.</returns>
		public object Create(InjectionContext context) {
			//Instantiate and return the object.
			var myObject = new MyObject();
			return myObject;
		}
	}
}

The InjectionContext object contains information about the current injection/resolution, which can be used to help deciding how the instance will be created by the factory.

To bind a type to a factory class, use the ToFactory():

//Binding factory by generics...
container.Bind<SomeType>().ToFactory<MyFactory>();
//...or type instance...
container.Bind<SomeType>().ToFactory(typeMyFactory);
//...or a factory instance.
container.Bind<SomeType>().ToFactory(factoryInstance);

Note: factories are resolved and injected by the container. So, it's possible to receive dependencies either by construtor and/or fields/properties.

Bindings setup

Sometimes the bindings list can become (very) large and bloat the SetupContainers() method of the context root. For better organization, it's possible to create reusable objects which will group and setup related bindings in a given container.

To create a bindings setup object, implement the Adic.IBindingsSetup interface:

using Adic;
using Adic.Container;

namespace MyNamespace.Bindings {
	/// <summary>
	/// My bindings.
	/// </summary>
	public class MyBindings : IBindingsSetup {
		public void SetupBindings (IInjectionContainer container) {
			container.Bind<SomeType>().ToSingleton<AnotherType>();
			//...more related bindings.
		}
	}
}

To perform a bindings setup, call the SetupBindings() method in the container, passing either the binding setup object as a parameter or the namespace in which the setups reside:

//Setup by generics...
container.SetupBindings<MyBindings>();
//...or by type...
container.SetupBindings(typeof(MyBindings));
//...or from an instance...
var bindings = MyBindings();
container.SetupBindings(bindings);
//...or using a namespace.
container.SetupBindings("MyNamespace.Bindings");

Note: the default behaviour of SetupBindings() with namespace is to use all IBindingsSetup objects under the given namespace and all its children namespaces. If you need that only IBindingsSetup objects in the given namespace are used, call the overload that allows indication of children namespace evaluation:

container.SetupBindings("MyNamespace.Bindings", false);

Binding setup priorities

The order of bindings setups matters. In case an IBindingsSetup object relies on bindings from another IBindingsSetup object, add the other setup first.

However, if you are using SetupBindings() with a namespace, it's not possible to manually order the IBindingsSetup objects. In this case, you have to decorate the IBindingsSetup classes with a BindingPriority attribute to define the priority in which each bindings setup will be executed:

using Adic;
using Adic.Container;

namespace MyNamespace.Bindings {
	/// <summary>
	/// My bindings.
	/// </summary>
	[BindingPriority(1)]
	public class MyBindings : IBindingsSetup {
		public void SetupBindings (IInjectionContainer container) {
			container.Bind<SomeType>().ToSingleton<AnotherType>();
			//...more related bindings.
		}
	}
}

Higher values indicate higher priorities. If no priority value is provided, the default value of 1 will be used.

Using commands

What are commands?

Commands are actions executed by your game, usually in response to an event.

The concept of commands on Adic is to place everything the action/event needs in a single place, so it's easy to understand and maintain it.

Suppose you have an event of enemy destroyed. When that occurs, you have to update UI, dispose the enemy, spawn some particles and save statistics somewhere. One approach would be dispatch the event to every object that has to do something about it, which is fine given it keeps single responsibility by allowing every object to take care of their part on the event. However, when your project grows, it can be a nightmare to find every place a given event is handled. That's when commands come in handy: you place all the code and dependencies for a certain action/event in a single place.

Creatings commands

To create a command, inherit from Adic.Command and override the Execute() method, where you will place all the code needed to execute the command. If you have any dependencies to be injected before executing the command, add them as fields or properties and decorate them with an Inject attribute:

namespace MyNamespace.Commands {
	/// <summary>
	/// My command.
	/// </summary>
	public class MyCommand : Adic.Command {
		/// <summary>Some dependency to be injected.</summary>
		[Inject]
		public object someDependency;

		/// <summary>
		/// Executes the command.
		/// </summary>
		/// <param name="parameters">Command parameters.</param>
		public override void Execute(params object[] parameters) {
			//Execution code.
		}
	}
}

Hint: it's also possible to wire any dependencies through constructor. However, in this case the dependencies will only be resolved once, during instantiation.

Good practice: place all your commands under the same namespace, so it's easy to register them.

Types of commands
Pooled

The command is kept in a pool for reuse, avoiding new instantiations. It's useful for commands that need to maintain state when executing. This is the default behaviour.

When creating pooled commands, it's possible to set the initial and maximum size of the pool for a particular command by setting, respectively, the preloadPoolSize and maxPoolSize properties:

namespace MyNamespace.Commands {
	/// <summary>
	/// My command.
	/// </summary>
	public class MyCommand : Adic.Command {
		/// <summary>The quantity of the command to preload on pool (default: 1).</summary>
		public override int preloadPoolSize { get { return 5; } }
		/// <summary>The maximum size pool for this command (default: 10).</summary>
		public override int maxPoolSize { get { return 20; } }

		/// <summary>
		/// Executes the command.
		/// </summary>
		/// <param name="parameters">Command parameters.</param>
		public override void Execute(params object[] parameters) {
			//Execution code.
		}
	}
}
Singleton

There's only one copy of the command available, which is used every time the command is dispatched. It's useful for commands that don't need state or every dependency the command needs is given during execution. To make a command singleton, return true in the singleton property of the command:

namespace MyNamespace.Commands {
	/// <summary>
	/// My command.
	/// </summary>
	public class MyCommand : Adic.Command {
		/// <summary>Indicates whether this command is a singleton (there's only one instance of it).</summary>
		public override bool singleton { get { return true; } }

		/// <summary>
		/// Executes the command.
		/// </summary>
		/// <param name="parameters">Command parameters.</param>
		public override void Execute(params object[] parameters) {
			//Execution code.
		}
	}
}

Note 1: this is the default command type.

Note 2: when using singleton commands, injection is done only through constructors or injection after command instantiation.

Registering commands

To register a command, call the Register() method on the container, usually in the context root:

using UnityEngine;

namespace MyNamespace {
	/// <summary>
	/// Game context root.
	/// </summary>
	public class GameRoot : Adic.ContextRoot {
		public override void SetupContainers() {
			//Create the container.
			this.AddContainer<InjectionContainer>()
				//Register any extensions the container may use.
				.RegisterExtension<CommanderContainerExtension>()
				//Registering by generics...
				.RegisterCommand<MyCommand>()
				//...or by type.
				.RegisterCommand(typeof(MyCommand));
		}

		public override void Init() {
			//Init the game.
		}
	}
}

Note: when registering a command, it's placed in the container, so it's easier to resolve it and its dependencies.

It's also possible to register all commands under the same namespace by calling the RegisterCommands() method in the container and passing the full name of the namespace:

public override void SetupContainers() {
	//Create the container.
		this.AddContainer<InjectionContainer>()
			//Register any extensions the container may use.
			.RegisterExtension<CommanderContainerExtension>()
			//Register all commands under the namespace "MyNamespace.Commands".
			.RegisterCommands("MyNamespace.Commands");
}

Note: the default behaviour of RegisterCommands() is to register all commands under the given namespace and all its children namespaces. If you need that only commands in the given namespace are registered, call the overload that allows indication of children namespace evaluation:

container.RegisterCommands("MyNamespace.Commands", false);

Dispatching commands

From code using Command Dispatcher

To dispatch a command, just call the Dispatch() method on Adic.ICommandDispatcher, using either the generics or the by System.Type versions:

/// <summary>
/// My method that dispatches a command.
/// </summary>
public void MyMethodThatDispatchesCommands() {
	//Dispatching by generics...
	dispatcher.Dispatch<MyCommand>();
	//...or by type.
	dispatcher.Dispatch(typeof(MyCommand));
}

It's also possible to dispatch a command after a given period of time by calling the InvokeDispatch() method:

//Timed dispatching by generics...
dispatcher.InvokeDispatch<MyCommand>(1.0f);
//...or by type.
dispatcher.InvokeDispatch(typeof(MyCommand), 1.0f);

To use Adic.ICommandDispatcher, you have to inject it wherever you need to use it:

namespace MyNamespace {
	/// <summary>
	/// My class that dispatches commands.
	/// </summary>
	public class MyClassThatDispatchesCommands {
		/// <summary>The command dispatcher.</summary>
		[Inject]
		public ICommandDispatcher dispatcher;

		/// <summary>
		/// My method that dispatches a command.
		/// </summary>
		public void MyMethodThatDispatchesCommands() {
			this.dispatcher.Dispatch<MyCommand>();
		}
	}
}

Hint: commands already have a reference to its dispatcher (this.dispatcher).

Note 1: when dispatching a command, it's placed in a list in the command dispatcher object, which is the one responsible for pooling and managing existing commands.

Note 2: commands in the pool that are not singleton are reinjected every time they are executed.

From code using CommandReference type

The Adic.CommandReference type allows the creation of properties on MonoBehaviour that represents a command that can be manually dispatched from code.

using Unity.Engine;

namespace MyNamespace {
	/// <summary>
	/// My MonoBehaviour summary.
	/// </summary>
	public class MyBehaviour : MonoBehaviour {
		/// <summary>Reference to the command. Can be edited on Inspector.</summary>
		public CommandReference someCommand;

		/// <summary>
		/// Manually dispatches the command.
		/// </summary>
		protected void DispatchCommand() {
			this.someCommand.DispatchCommand();
		}
	}
}
From game objects

It's possible to dispatch commands directly from game objects without the need to write any code using the components available in the Commander extension.

To use them, just add the desired component to a game object.

Command Dispatch

Provides a routine to call a given command. The routine name is DispatchCommand().

Using this component, you can e.g. call the DispatchCommand() method from a button in the UI or in your code.

It can be found at the Component/Adic/Commander/Command dispatch menu.

Timed Command Dispatch

Dispatches a command based on a timer.

This component also provides the DispatchCommand() routine, in case you want to call it before the timer ends.

It can be found at the Component/Adic/Commander/Timed command dispatch menu.

Retaining commands

When a command needs to continue its execution beyond the Execute() method, it has to be retained. This way the command dispatcher knows the command should only be pooled/disposed when it finishes its execution.

This is useful not only for commands that implement Adic.IUpdatable, but also for commands that have to wait until certain actions (e.g. some network call) are completed.

To retain a command, just call the Retain() method during main execution:

/// <summary>
/// Executes the command.
/// </summary>
/// <param name="parameters">Command parameters.</param>
public override void Execute(params object[] parameters) {
	//Execution code.

	//Retains the command until some long action is completed.
	this.Retain();
}

If a command is retained, it has to be released. The command dispatcher will automatically releases commands during the destruction of scenes. However, in some situations you may want to release it manually (e.g. after some network call is completed).

To release a command, just call the Release() method when the execution is finished:

/// <summary>
/// Called when some action is finished.
/// </summary>
public void OnSomeActionFinished() {
	//Releases the command.
	this.Release();
}

It's also possible to manually release all commands of a specified type by calling the Release() method of the CommandDispatcher:

//Releasing all commands of a given type by generics...
dispatcher.ReleaseAll<SomeCommand>();
//...or by type instance...
dispatcher.ReleaseAll(typeof(SomeCommand));

Timed invoke

It's possible to use timed method invocation inside a command by calling the Invoke() method:

namespace MyNamespace.Commands {
	/// <summary>
	/// My command.
	/// </summary>
	public class MyCommand : Adic.Command {
		/// <summary>
		/// Executes the command.
		/// </summary>
		/// <param name="parameters">Command parameters.</param>
		public override void Execute(params object[] parameters) {
			//Invokes the "MyMethodToInvoke" method after 1 second.
			this.Invoke(this.MyMethodToInvoke, 1.0f);

			//Retains the command until the invocation is finished.
			this.Retain();
		}

		/// <summary>
		/// My method to invoke.
		/// </summary>
		private void MyMethodToInvoke() {
			//Method code.

			//Releases the command after the invocation.
			this.Release();
		}
	}
}

Note 1: when an invocation is scheduled, the command is automatically retained. So, when the invocation method is called, always release the command.

Note 2: when a command is released, all invocations are discarded.

Coroutines

It's possible to use coroutines inside a command by calling the StartCoroutine() method:

namespace MyNamespace.Commands {
	/// <summary>
	/// My command.
	/// </summary>
	public class MyCommand : Adic.Command {
		/// <summary>
		/// Executes the command.
		/// </summary>
		/// <param name="parameters">Command parameters.</param>
		public override void Execute(params object[] parameters) {
			//Starts the coroutine.
			this.StartCoroutine(this.MyCoroutine());

			//Retains the command until the coroutine is finished.
			this.Retain();
		}

		/// <summary>
		/// My coroutine.
		/// </summary>
		private IEnumerator MyCoroutine() {
			//Coroutine code.

			//Releases the command after execution.
			this.Release();
		}
	}
}

If needed, it's also possible to stop a coroutine after it's started by calling the StopCoroutine() method.

Note 1: when a coroutine is started, the command is automatically retained. So, when the coroutine completes its execution, always release the command.

Note 2: when a command is released, all coroutines started from that command are stopped.

A note about scene destruction and commands

When a scene is destroyed, all commands will be released and all registrations will be disposed.

So, if you're using a container that will live through scenes, be aware that all commands will have to be registered again.

Multiple scenes

There are different strategies when working with Adic in a multiple scene architecture, each one offering its own advantages.

All strategies are related to how the context root and the lifetime of the container(s) are used.

Single context root for all scenes

The game uses a single context root for all scenes. In this strategy, all bindings are recreated each time a scene is loaded.

It's useful for games that use a single scene or when the recreation of the bindings is not an issue. This is the default strategy, as seem on Quick start.

One context root per scene

The game has one context root per scene, each one with its own container(s). In this case, it's important to use bindings setup to share bindings among all containers and a single reflection cache to improve performance and memory consumption.

It's useful for games that have different bindings per scene.

Shared container

A single container is shared among all scenes. In this strategy, it's common to have a single context root that is executed only on the first scene.

To use a shared container, when adding containers using AddContainer(), keep them alive between scenes by setting the destroyOnLoad to false.

It's useful for games that will always use the same bindings across all scenes.

Note: when using a shared container, it's recommended to only bind singleton instances of objects that should live up through all scenes (e.g. a common service) to prevent missing references - when a scene is destroyed, eventually some singleton objects that are bound to the container may not exist in the new scene anymore. Also, factories that rely on state to create their objects could also be affected by missing references.

Additive scenes

A single context root is executed in the first scene and all other scenes are loaded additively.

To load a scene additively, please consult Application.LoadLevelAdditiveAsync on the Unity Documentation.

It's useful for games that use different scenes for each part of the level but share the same bindings across all of those scenes.

A combination of all of the above

Each game has its own characteristics, and eventually the same game could feature more than one multiple scene strategy. It's important to test which one would suit the needs of the project and use different strategies when required.

Order of events

  1. Unity Awake()
  2. ContextRoot calls SetupContainers()
  3. ContextRoot asks for each container to generate cache for its types
  4. ContextRoot calls Init()
  5. Unity Start() on all MonoBehaviour
  6. Injection on MonoBehaviour
  7. Update() is called on every object that implements Adic.IUpdatable
  8. Scene is destroyed
  9. Dispose() is called on every object that implements System.IDisposable

Script execution order

Sometimes you may face some strange exceptions about null containers, even with containers correctly configured. This may occur because of the Script Execution Order of the injected scripts, which are being called before the ContextRoot creates the containers.

To prevent this from happening, the execution order of the context root should be set by either clicking on the Set execution order button on the context root inspector or by accessing the menu Edit > Project Settings > Script Execution Order on Unity.

Performance

Adic was created with speed in mind, using internal cache to minimize the use of reflection (which is usually slow), ensuring a good performance when resolving and injecting into objects - the container can resolve a 1.000 objects in 1ms*.

To maximize performance:

  1. always bind all types that will be resolved/injected in the ContextRoot, so Adic can generate cache of the objects and use that information during runtime.

  2. always create all game objects that will be used during runtime before the game starts. Object pooling can help achieve that and increase performance by creating (and injecting) game objects upfront and reusing them throughout the game.

  3. when injecting on MonoBehaviour, use scene wide injection during game start instead of per MonoBehaviour injection. Read MonoBehaviour injection for more details about injecting on the entire scene.

  4. if you have more than one container on the same scene, it's possible to share cache between them. To do so, create an instance of Adic.Cache.ReflectionCache and pass it to any container you create:

using UnityEngine;

namespace MyNamespace {
	/// <summary>
	/// Game context root.
	/// </summary>
	public class GameRoot : Adic.ContextRoot {
		public override void SetupContainers() {
			//Create the reflection cache.
			var cache = new ReflectionCache();

			//Create a new container.
			var container1 = this.AddContainer(new InjectionContainer(cache));

			//Container configurations and bindings...

			//Create a new container.
			var container2 = this.AddContainer(new InjectionContainer(cache));

			//Container configurations and bindings...
		}

		public override void Init() {
			//Init the game.
		}
	}
}

* See Tests/Editor/SpeedTest.cs for more details on performance tests. Tested on a MacBook Pro late 2014 (i7 2.5/3.7 GHz).

- A thousand simple resolves in 1ms
- A million simple resolves in 1330ms
- A thousand complex resolves in 2ms
- A million complex resolves in 2428ms

A simple resolve is the resolution of a class without any Inject attributes.
A complex resolve is the resolution of a class that is not bound to the container and has a Inject attribute in a field.

IL2CPP, AOT and code stripping

When compiling to AOT platforms using IL2CPP (like iOS and WebGL), Unity performs code stripping, removing any code that is not being used. Although this is useful to reduce build size, it also affects classes that are only instantiated through Adic, since they are not created directly. To prevent non-inclusion of these classes, Unity provides the Preserve attribute, which should be added to all classes that are only created through the container.

General notes

  1. If an instance is not found, it will be resolved to NULL.
  2. Multiple injections must occur in an array of the desired type.
  3. Order of bindings is controlled by just reordering the bindings during container setup.
  4. Avoid singleton bindings of objects that will be destroyed during execution. This can lead to missing references in the container.
  5. Any transient object, once resolved, is not tracked by Adic. So, if you want e.g. a list of all prefabs that were resolved by the container, you'll have to store it manually. Singleton objects are kept inside the container, given there is just a single instance of them.
  6. Adic relies on Unity Test Tools for unit testing. You can download it at Unity Asset Store.

Extensions

Extensions are a way to enhance Adic without having to edit it to suit different needs. By using extensions, the core of Adic is kept agnostic, so it can be used on any C# environment.

Available extensions

Bindings Printer

Prints all bindings on any containers on the current context root. It must be executed on Play Mode.

To open the Bindings Printer windows, click on Windows/Adic/Bindings Printer menu.

Format

[Container Type Full Name] (index: [Container Index on ContextRoot], [destroy on load/singleton])

	[For each binding]
	Type: [Binding Type Full Name]
	Bound to: [Bound To Type Full Name] ([type/instance])
	Binding type: [Transient/Singleton/Factory]
	Identifier [Identifier/-]
	Conditions: [yes/no]

Dependencies

Bindings Setup

Provides a convenient place to setup bindings and reuse them in different containers and scenes.

Configuration

Please see Bindings setup for more information.

Dependencies

None

Commander

Provides dispatching of commands, with pooling for better performance.

For more information on commands, see Using commands.

Configuration

Register the extension on any containers that will use it:

//Create the container.
this.AddContainer<InjectionContainer>()
	//Register any extensions the container may use.
	.RegisterExtension<CommanderContainerExtension>();

If you use IDiposable or IUpdatable events, also register the Event Caller extension:

//Create the container.
this.AddContainer<InjectionContainer>()
	//Register any extensions the container may use.
	.RegisterExtension<CommanderContainerExtension>()
	.RegisterExtension<EventCallerContainerExtension>();

Dependencies

Context Root

Provides an entry point for the game on Unity.

Configuration

Please see Quick start for more information.

Dependencies

None

Event Caller

Calls events on classes that implement certain interfaces. The classes must be bound to a container.

Available events

Update

Calls Update() method on classes that implement Adic.IUpdatable interface.

namespace MyNamespace {
	/// <summary>
	/// My updateable class.
	/// </summary>
	public class MyUpdateableClass : Adic.IUpdatable {
		public void Update() {
			//Update code.
		}
	}
}
Dispose

When a scene is destroyed, calls Dispose() method on classes that implement System.IDisposable interface.

namespace MyNamespace {
	/// <summary>
	/// My disposable class.
	/// </summary>
	public class MyDisposableClass : System.IDisposable {
		public void Dispose() {
			//Dispose code.
		}
	}
}

Configuration

Register the extension on any containers that will use it:

//Create the container.
this.AddContainer<InjectionContainer>()
	//Register any extensions the container may use.
	.RegisterExtension<EventCallerContainerExtension>();ge

Notes

  1. Currently, any objects that are updateable are not removed from the update's list when they're not in use anymore. So, it's recommended to implement the Adic.IUpdatable interface only on singleton or transient objects that will live until the scene is destroyed;
  2. When the scene is destroyed, the update's list is cleared. So, any objects that will live between scenes that implement the Adic.IUpdatable interface will not be readded to the list. It's recommeded to use updateable objects only on the context of a single scene.
  3. Be aware of singleton objects on containers that will live through scenes. Eventually these objects may try to use references that may not exist anymore.

Dependencies

None

Mono Injection

Allows injection on MonoBehaviour by providing an Inject() method.

Configuration

Please see MonoBehaviour injection for more information.

Dependencies

State Injection

Allows injection on StateMachineBehaviour by providing an Inject() method.

Configuration

Please see StateMachineBehaviour injection for more information.

Dependencies

Unity Binding

Provides Unity bindings to the container.

Please see Bindings for more information.

Configuration

Register the extension on any containers that you may use it:

//Create the container.
this.AddContainer<InjectionContainer>()
	//Register any extensions the container may use.
	.RegisterExtension<UnityBindingContainerExtension>();

Notes

  1. ALWAYS CALL Inject() FROM 'Start'! (use the Mono Injection Extension).

Dependencies

None

Creating extensions

Extensions on Adic can be created in 3 ways:

  1. Creating a framework extension extending the base APIs through their interfaces;
  2. Creating extension methods to any part of the framework;
  3. Creating a container extension, which allows for the interception of internal events, which can alter the inner workings of the framework.

Note: always place the public parts of extensions into Adic namespace.

To create a container extension, which can intercept internal Adic events, you have to:

1. Create the extension class with ContainerExtension sufix.

2. Implement Adic.Container.IContainerExtension.

3. Subscribe to any events on the container on OnRegister method.

public void OnRegister(IInjectionContainer container) {
	container.beforeAddBinding += this.OnBeforeAddBinding;
}

4. Unsubscribe to any events the extension may use on the container on OnUnregister method.

public void OnUnregister(IInjectionContainer container) {
	container.beforeAddBinding -= this.OnBeforeAddBinding;
}

Container events

Container events provide a way to intercept internal actions of the container and change its inner workings to suit the needs of your extension.

All events are available through Adic.InjectionContainer.

Binder events

  • beforeAddBinding: occurs before a binding is added.
  • afterAddBinding: occurs after a binding is added.
  • beforeRemoveBinding: occurs before a binding is removed.
  • afterRemoveBinding: occurs after a binding is removed.

Injector events

  • beforeResolve: occurs before a type is resolved.
  • afterResolve: occurs after a type is resolved.
  • bindingEvaluation: occurs when a binding is available for resolution.
  • bindingResolution: occures when a binding is resolved to an instance.
  • beforeInject: occurs before an instance receives injection.
  • afterInject: occurs after an instance receives injection.

Binaries

If you need compiled binaries of Adic, look for them at the releases page (starting from version 2.13).

The project is divided into 2 binaries:

  1. Adic.Framework.dll: main framework, decoupled from Unity.
  2. Adic.Extensions.dll Unity extensions.

Examples

There are some examples that are bundled to the main package that teach the basics and beyond of Adic.

1. Hello World

Exemplifies the basics of how to setup a scene for dependency injection using the ContextRoot.

2. Binding Game Objects

Exemplifies how to bind components to new and existing game objects and allows them to share dependencies.

3. Using conditions

Exemplifies the use of condition identifiers on injections.

4. Prefabs

Exemplifies how to bind to prefabs and the use of method injection as a second constructor.

5. Commander

Exemplifies the use of commands through a simple spawner of a prefab.

6. Bindings Setup

Exemplifies the use of bindings setups to better organize the bindings for a container.

7. Factory

Exemplifies the use of a factory to create and position cubes as a matrix.

Changelog

Please see CHANGELOG.txt.

Support

Found a bug? Please create an issue on the GitHub project page or send a pull request if you have a fix or extension.

You can also send me a message at [email protected] to discuss more obscure matters about Adic.

License

Licensed under the The MIT License (MIT). Please see LICENSE for more information.