2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【WPF Prism】 DIの遅延解決

Last updated at Posted at 2022-03-06

はじめに

最近、ReactiveUIのDependency Injection(Splat)について調べてzennに書いた

Splatでは、「オンデマンドでインスタンスを生成する方法」が使える

Splat supports on-demand new'ing, constant and lazy registration of dependencies.

// デリゲートを登録
Locator.CurrentMutable.Register(() => new Person(), typeof(IPerson));

// 名前解決
var person = Locator.Current.GetService<IPerson>();

必要になるまでインスタンスを生成しないようコントロールできるので、メモリ節約の面で利点がある

この記事は

先述のSplatによるインスタンス遅延解決をPrismで試す

  • .NET Core 3.1
  • Prism.Unity 8.0.0.1909

Prism Library の遅延解決

Prism 8 以降は遅延解決の機能を標準実装しているらしい

Lazy Resolution - Prism Documentation

ふつうのコンストラクタインジェクション

まずは比較のために、ふつうの方法

MainWindowViewModelが呼ばれたタイミングで、引数のpersonが生成されている

public class MainWindowViewModel : BindableBase
{
    IPerson person;

    public MainWindowViewModel(Person person)
    {
        Debug.WriteLine("1");
        this.person = person;
        Debug.WriteLine("2");
    }
}

/* output
Person Constructor Called!!
1
2
*/

メンバー変数this.personへ代入する以前にコンストラクターが生成されて``Person Constructor Called!!`が表示されている

遅延解決をつたったコンストラクターインジェクション

MainWindowViewModelの引数にはLazy<T>クラスが渡される

インスタンス化するには.Valueをつける

public MainWindowViewModel(Lazy<Person> factory)
{
    Debug.WriteLine("1");
    var f = factory;
    Debug.WriteLine("2");
    this.person = f.Value;
    Debug.WriteLine("3");
}
/* output
1
2
Person Constructor Called!!
3
*/

メンバー変数this.personへ代入するタイミングでインスタンスが生成されてCalled!!メッセージが表示されている

・・・ここで間違いに気づく

コンストラクターインジェクションをするならば、***ViewModelの引数にインターフェイスIPersonを渡さねばらない

本来ここまで書いたViewModelのコンストラクターの引数カッコ内にはIPersonが存在するはず

// 遅延解決ならこう書くし
public MainWindowViewModel(Lazy<IPerson> factory)

// ふつうのコンストラクターインジェクションならこう書く
public MainWindowViewModel(IPerson person)

そもそもAppクラスのvoid RegisterTypes(IContainerRegistry containerRegistry)メソッドで登録作業をしていなかった

しかし、見た感じは想定通りに動いていた・・なぜ?

ViewModelの引数にインターフェイスを渡す

試しにDI登録していない状態で引数をインターフェイスにしたらどうなるのか確認する

Lazy<T>を使った遅延解決の場合

IPerson型のコンストラクターがありません」エラー

IPerson person;

public MainWindowViewModel(Lazy<IPerson> factory)
{
    this.person = factory.Value; // ここで例外発生
}

Unity.ResolutionFailedException: 'Resolution failed with error: No public constructor is available for type FullApp.IPerson.

Lazy<T>を使わない場合

内部例外2に同じ例外メッセージが書かれている

public MainWindowViewModel(IPerson person) // ここで例外発生
{
}

Message='プロパティ 'Prism.Mvvm.ViewModelLocator.AutoWireViewModel' の Set で例外がスローされました。

内部例外 1:
ContainerResolutionException: An unexpected error occurred while resolving 'FullApp.ViewModels.MainWindowViewModel'

内部例外 2:
ResolutionFailedException: Resolution failed with error: No public constructor is available for type FullApp.IPerson.

内部例外 3:
InvalidOperationException: No public constructor is available for type FullApp.IPerson.

内部例外 4:
InvalidRegistrationException:

AppクラスでDI登録する

containerRegistry.RegisterSingleton<IPerson, Person>();

想定通りの結果になった(上記で示したとおりのコンソール出力だった)

ほぼ内容が同じで重複するので掲載は割愛する

DI登録しなくてもインスタンスがViewModelに渡る仕組み

ViewModelLocator - Prism Library

ここを読んだだけではよくわからず、OSSなのでソースを見てみる

長くなる&本題から外れるのでここまで →つづき

おわりに

Prismライブラリの遅延解決は簡単だった

また、
ReactiveUIのSplatでは登録時にあらかじめ遅延させるか決める必要があったが
Prism.Unityでは呼び出す側で遅延する/しないを決められる

という違いがあることがわかった

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?