Skip to content

bit34/Bit34-Injector

Repository files navigation

Bit34 Injector - Dependency Injection Library

Table of contents

What is it?

This is a C# dependency injection (DI) library with a small set of features.

Who is it for?

It is primarily developed to be used in conjunction with our other libraries. Its simple nature also makes it an easy point to start learning DI.

What does dependency injection do anyway?

If you are new to DI just jump into this Unity3D tutorial project to learn its usage and advantages.

Basic example

using System;
using Com.Bit34Games.Injector;

//  A class that we need to access its reference in other classes
class UserData
{
    public string name;

    public UserData()
    {
        this.name = "Guest";
    }
}

//  A target class that will be injected into
class MyWindow
{
    [Inject] private UserData _userData;

    public void Show()
    {
        Console.WriteLine("Hello " + _userData.name);
    }
}

class Program
{
    static void Main(string[] args)
    {
        //  Create injector
        IInjector InjectorContext = new InjectorContext(true);

        //  Add bindings
        UserData myUser = new UserData();
        myUser.name = "It is me";
        injector.AddBinding<UserData>().ToValue(myUser);

        //  Inject into targets
        MyWindow window = new MyWindow();
        injector.InjectInto(window);

        //  All set
        window.Show();
    }
}

Documentation


Design Decisions

  • Only singleton bindings; every binded type will share same instance.
  • Bind first, use later; after making first injection you can not make any more bindings and it will cause an error.

Creating Injector

Constructor parameter shouldThrowException allows you to choose error handling behavior.

When set to true, InjectorContext throws an exception when an error occurs. This should be your default choice for development cause it will let you find errors sooner.

When set to false, InjectorContext internally stores error messages for later examinations. This behavior is added for development purposes. Even though it is not advised, you can use this option on production to manually catch unexpected errors.


Adding Bindings

Binding is implemented as a fluent api to make it easy to write and read.

To add a new binding you start by calling AddBinding generic method with the binding type that you want to inject in classes.

injector.AddBinding<MyClass>()

It returns an object with two methods.

ToValue method allows you to bind an already instantiated provider object to binding type.

MyClass myClass = new MyClass();
injector.AddBinding<MyClass>().ToValue(myClass);

ToType method allows you to bind a provider type that will be instantiated the first time it is needed.

injector.AddBinding<MyClass>().ToType<MyClass>();

PS: Provider type must have a parameterless constructor or a constructor that has default values for all off its parameters.

Assignable and multiple bindings

You can bind a binding type to assignable provider type or value (interface implementations to interfaces, child classes to parent classes).

Lets say you have a Setting class that implements read only ISettings interface.

interface ISettings
{
    float Volume { get; }
}

class Settings : ISettings
{
    public float Volume { get; set; }
}

You can use interface as binding type since its implementations are assignable to it.

injector.AddBinding<ISettings>().ToType<Settings>();

Actually you can bind multiple binding types to same value or provider type if they are assignable. They will share the same instance of Settings class.

injector.AddBinding<Settings>().ToType<Settings>();
injector.AddBinding<ISettings>().ToType<Settings>();

Some uses of this practice are;

  • Using read only and mutable interfaces of a type to prevent accedental writes (e.g. injecting Settings in SettingsWindow, injecting readonly ISettings interface everywhere else)
  • Binding platform specific implementations and to a common interface to provide abstraction (e.g. injecting IPlatform in everywhere, binding it to WindowsPlatform or LinuxPlatform implementations depending on your target)
  • Using debugging friendly implementations of interfaces to find or isolate errors faster (e.g. an implementaion that adds verbose logging)
  • Provide a dummy implementations of a class before its actual implementation is available (e.g. injecting IHighScoreServer, using MockHighScoreServer until it is provided )

Binding Restrictions

TBA


Injection

After you complete your injections all you have to do is to using InjectInto to target objects. This will set all fields and properties that has Inject attribute.

injector.InjectInto(window);
injector.InjectInto(settingsPanel);
injector.InjectInto(playerManager);

Manually getting instances

Other than injections InjectorContext has methods to access instances directly. Those are usefully for initilization methods after bindings are completed.

T GetInstance<T>() methods gets the instance of that bindind type

injector.GetInstance<ISaveManager>().LoadSaves();

IEnumerator<T> GetAssignableInstances<T>() method checks all provider types and values and collect if they are assignable to given type;

IEnumerator<IManager> managers = injector.GetAssignableInstances<IManager>();

while(manager.MoveNext())
{
    managers.Current.Initialize();
}

Error Handling

When InjectorContext is set to NOT to throw errors in constructor you can use ErrorCount property and GetError() method any time to inspect stored errors in InjectorContext.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages