【C#】Unity Container(IoC Container)の使い方

# 概要
Unity Containerの解説は英語ばかりで、日本語の解説が少ないため投稿した。

# 前置き
1.ゲームエンジンのUnityではありません。念のため。
2.本記事では使い方にフォーカスするため、IoC(制御の反転)、DIP(依存性逆転の原則)、依存性注入(DI)、IoCコンテナといった用語の説明はあまりしません。

下記がわかりやすいので、そちらを参照してください。
[DI (依存性注入) って何のためにするのかわからない人向けに頑張って説明してみる](https://qiita.com/okazuki/items/a0f2fb0a63ca88340ff6)

# 実行環境
.NET Framework 4.6.1
Unity Container 5.11.10

# インストール
「ソリューションのNugetパッケージの管理」→「Unity」で検索
![2021-04-12-Nuget-Unity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/a4e1ec6b-ff52-469d-6bf6-3a5db4923929.png)

#Unity Containerサンプル-IFの利用者が単数-

## クラス図
IFを定義し、DriverはIVehicleに依存し、Car/BikeはIVehicleに依存させる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/dd564b37-905a-83d6-38f0-9f9a4427cf85.png)

## クラスの実装
Dependency Injectionパターンのコンストラクタインジェクションを使う。
コンストラクタで外部から具象クラスインスタンスを渡して、振る舞いを変える。

~~~C#
public interface IVehicle
{
    int Run();
}

public class Car : IVehicle
{
    private int _miles = 0;
    public int Run()
    {
        return ++_miles;
    }
}

public class Bike : IVehicle
{
    private int _miles = 0;

    public int Run()
    {
        return ++_miles;
    }
}

public class Driver
{
    private IVehicle _vehicle = null;
    [InjectionConstructor]
    public Driver(IVehicle vehicle )
    {
        _vehicle = vehicle;
    }

    public void Run()
    {
        Console.WriteLine($"Run {_vehicle.GetType().Name} - {_vehicle.Run()} mile");
    }
}
~~~

# Unity Containerの適用
Dependency Injectionパターンのコンストラクタインジェクションを使う上で、下記作業が必要になる。
これをUnity Containerにやってもらう。
・抽象クラスと具象クラスの対応付け
・具象クラスに応じたインスタンスを提供する。

## 1. Unity Containerインスタンスを作成する
~~~C#
using System;
using Unity;
using Unity.Injection;

static void Main(string[] args)
{
    IUnityContainer container = new UnityContainer();
//(略)
}
~~~

## 2. Register -[抽象クラス]と[具象クラス]をマッピング-

~~~C#
static void Main(string[] args)
{
//(略)
// 同じ型に対して、複数の型情報を登録するため名前付きにする
container.RegisterType<IVehicle, Car>(nameof(Car));
container.RegisterType<IVehicle, Bike>(nameof(Bike));
container.RegisterType<Driver>(nameof(Car) + "Driver", new InjectionConstructor(container.Resolve<IVehicle>(nameof(Car))));
container.RegisterType<Driver>(nameof(Bike) + "Driver", new InjectionConstructor(container.Resolve<IVehicle>(nameof(Bike))));
//(略)
}
~~~

## 3.Resolove -マッピング情報を基に依存性を注入-

~~~C#
static void Main(string[] args)
{
//(略)
    if (container.Resolve<IVehicle>(nameof(Car)) is Car)
    {
        Console.WriteLine("Return Car");
    }
    if(container.Resolve<IVehicle>(nameof(Bike)) is Bike)
    {
        Console.WriteLine("Return Bike");
    }
    var CarDriver = container.Resolve<Driver>(nameof(Car) + "Driver");
    CarDriver.Run();
    var BikeDriver = container.Resolve<Driver>(nameof(Bike) + "Driver");
    BikeDriver.Run();
//(略)
}
~~~

実行結果

~~~C#
Return Car
Return Bike
Run Car - 1 mile
Run Bike - 1 mile
~~~

# Unity Container適用サンプル-IFの利用者が複数-

## クラス図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/5fc5e3fd-0316-7fb8-72a9-83c1386700a6.png)

## 実装
~~~C#
class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        // 同じ型に対して、複数の型情報を登録するため名前付きにする
        container.RegisterType<IVehicle, Car>(nameof(Car));
        container.RegisterType<IVehicle, Bike>(nameof(Bike));
        container.RegisterType<CarDriver>(
            new InjectionConstructor(
                new ResolvedParameter<IVehicle>(nameof(Car))
                )
            );
        container.RegisterType<BikeDriver>(
            new InjectionConstructor(
                new ResolvedParameter<IVehicle>(nameof(Bike))
            )
        );

        if(container.Resolve<IVehicle>(nameof(Car)) is Car)
        {
            Console.WriteLine("Return Car");
        }
        if(container.Resolve<IVehicle>(nameof(Bike)) is Bike)
        {
            Console.WriteLine("Return Bike");
        }

        var carDriver = container.Resolve<CarDriver>();
        if (carDriver is  CarDriver)
        {
            carDriver.Run();
        }
        var bikeDriver = container.Resolve<BikeDriver>();
        if (bikeDriver is BikeDriver)
        {
            bikeDriver.Run();
        }
    }
}

public interface IVehicle
{
    int Run();
}

public class Car : IVehicle
{
    private int _miles = 0;
    public int Run()
    {
        return ++_miles;
    }
}

public class Bike : IVehicle
{
    private int _miles = 0;

    public int Run()
    {
        return ++_miles;
    }
}

public abstract class Driver
{
    protected  IVehicle Vehicle = null;
    public void Run()
    {
        Console.WriteLine($"Run {Vehicle.GetType().Name} - {Vehicle.Run()} mile");
    }
}

public class CarDriver : Driver
{
    [InjectionConstructor]
    public CarDriver(IVehicle vehicle)
    {
        this.Vehicle = vehicle;
    }
}

public class BikeDriver : Driver
{
    [InjectionConstructor]
    public BikeDriver(IVehicle vehicle)
    {
        this.Vehicle = vehicle;
    }
}
~~~
## 実行結果

~~~C#
Return Car
Return Bike
Run Car - 1 mile
Run Bike - 1 mile
~~~

# Unity Containerのパフォーマンスに関して
GitにIoC Containerのパフォーマンスの一覧が公開されている。
[IoC Performance](https://github.com/danielpalme/IocPerformance)

# 参考
[Unty Container Documentation](http://unitycontainer.org/index.html)
[Tutorials Teacher](https://www.tutorialsteacher.com/ioc/unity-container)
[StackOverFlow](https://stackoverflow.com/questions/42270297/resolve-names-registration-from-unity-which-creating-a-controller)