# 【WPF】DIコンテナのUnityを触ってみた
# はじめに
今月からソフトウェア開発のお仕事をすることになり、業務寄りの知識を勉強しようと思ったので、DIコンテナの Unity を触ってみることにしました。
DIコンテナ自体は、最近書いているAndroidアプリの方で Dagger を触っているので、別に初めてではないのですが、WPFアプリケーションでは使ったことがないので、Prism や XamlBehaviorsWpf の使い方のおさらいも兼ねて簡単なアプリを作ってみました。
リポジトリは こちら です。
このサンプルアプリの説明などは README.md を見ればある程度載っていますので、ここでは軽いメモ程度にとどめておこうと思います。
# DIしている部分の軽い解説など
# クラス図とコード
クラス図とコードは次のようになりました。
(本質的でない部分のコードは一部削っています。)
/* Girlfriend.cs */
using System;
namespace UnityContainerApp.Models
{
public abstract class Girlfriend
{
public abstract string Name { get; }
}
}
/* Marisa.cs */
using System;
namespace UnityContainerApp.Models
{
public sealed class Marisa : Girlfriend
{
public override string Name { get; } = "魔理沙";
}
}
/* Alice.cs */
using System;
namespace UnityContainerApp.Models
{
public sealed class Alice : Girlfriend
{
public override string Name { get; } = "アリス";
}
}
/* Boyfriend.cs */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnityContainerApp.Models
{
public sealed class Boyfriend
{
public string Name { get; } = "aridai";
}
}
/* Couple.cs */
using System;
using Unity;
namespace UnityContainerApp.Models
{
public sealed class Couple
{
[Dependency]
internal Boyfriend boyfriend;
public Girlfriend Girlfriend { get; }
public Boyfriend Boyfriend => this.boyfriend;
[InjectionConstructor]
public Couple([Dependency]Girlfriend girlfriend)
{
this.Girlfriend = girlfriend;
}
public override string ToString() => $"{this.Boyfriend.Name}と{this.Girlfriend.Name}はカップルです。";
}
}
/* MainWindowViewModel.cs */
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Prism.Interactivity.InteractionRequest;
using Prism.Mvvm;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using Unity;
using UnityContainerApp.Models;
namespace UnityContainerApp.ViewModels
{
public sealed class MainWindowViewModel : BindableBase
{
public string Text { get; }
[InjectionConstructor]
public MainWindowViewModel([Dependency]Couple couple)
{
this.Text = couple.ToString();
}
}
}
/* App.xaml.cs */
using System;
using System.Windows;
using Prism.Mvvm;
using Unity;
using UnityContainerApp.Models;
using UnityContainerApp.ViewModels;
namespace UnityContainerApp
{
public partial class App : Application
{
private readonly IUnityContainer container = new UnityContainer();
protected override void OnStartup(StartupEventArgs e)
{
#if DEBUG
this.RegisterDependenciesForDebug();
#else
this.RegisterDependenciesForRelease();
#endif
ViewModelLocationProvider.SetDefaultViewModelFactory(type => this.container.Resolve(type));
}
private void RegisterDependenciesForRelease()
{
this.container.RegisterType<Girlfriend, Marisa>();
}
private void RegisterDependenciesForDebug()
{
this.container.RegisterType<Girlfriend, Alice>();
}
}
}
# やっていること
見ての通り、彼女 (Girlfriend
) を抽象化しています。
ReleaseビルドとDebugビルド・テストで、具象型の魔理沙 (Marisa
) と アリス (Alice
) の2つを差し替えるようにしています。
彼女の依存性の登録は this.container.RegisterType<Girlfriend, Marisa>();
の部分で行っています。
Marisa
も Alice
も引数なしコンストラクタを持つ具象型ですので、DIコンテナが内部でインスタンスを生成でき、Girlfriend
の型を解決するときに、それぞれのインスタンスを返してくれます。
もし、引数なしコンストラクタがない場合でも、コンストラクタの引数がDIコンテナが解決できる型のみで構成されているのであれば、手動で登録する必要はありません。
この例で言えば Couple
と MainWindowViewModel
がそうです。
どちらも、コンストラクタインジェクションをしているので、コンストラクタに引数を指定する必要があります。
しかし、Couple
のコンストラクタは Girlfriend
が、MainWindowViewModel
のコンストラクタは Couple
がDIコンテナが解決できる型なので、コードでは手動で登録処理を書いていません。
もし書くとするならば、次のようになるでしょう。
this.container.RegisterType<Girlfriend, Marisa>();
this.container.RegisterInstance<Boyfriend>(new Boyfriend());
this.container.RegisterInstance<Couple>(new Couple(this.container.Resolve<Girlfriend>()));
this.container.RegisterInstance<MainWindowViewModel>(new MainWindowViewModel(this.container.Resolve<Couple>()));
// インスタンスを生成するのに必要なコンストラクタの引数を、DIコンテナが把握できているため、書かなくても勝手にやってくれる↑
また、Boyfriend
はフィールドインジェクションで注入しています。