MAUIがリリースされる前にAvaloniaUIのDIを試す
【前置き】MAUIとAvaloniaの比較
どちらもC#でクロスプラットフォームアプリケーションをつくるためのフレームワーク
MAUIは2022年の夏ごろにリリース予定(プレビュー版はすでに試せる)
比較項目 | MAUI | Avalonia |
---|---|---|
開発元 | マイクロソフト | OSS |
対応プラットフォーム | Android、iOS、macOS、Windows, Linux[1] | + WASM[2] |
両フレームワークを比較している記事
-
.Net MAUI or AvaloniaUI? - reddit
- ※注意※ 1年前の投稿であるため、Avaloniaがまだモバイル対応していない前提で話がされている
-
Introducing .NET Multi-platform App UI (MAUI) VS Avalonia
- GitHubの更新頻度など、活発さの度合を比較できる
Avaloniaを学ぶモチベーション
個人的にはマイクロソフトがサポートしているMAUIを選びたくなる
しかしながら、Avaloniaの公式HPを読んでまだ日が浅いものの、XAMLの記述がかなり簡素にできそうな気がしている
Avaloniaの良い点はMAUIにも活かせる(と信じて)、MAUIリリースまでにAvaloniaを学ぶ
本記事ではDependency Injectionの機能を試す
なぜDI?
AvaloniaUIはXAML側のフレームワークなので、DIから入るのは本来おかしい話だけれども・・・
依存性をうまく解決(隠ぺい)するにはDIの補助が不可欠なので、先に確認したい
以上が前置きで、次章から本題に入る
Avaloniaプロジェクトを作成する
Visual Sutdioの拡張機能をつかって、MVVMテンレートプロジェクトを作成する
【本題】Dependency Injection
AvaloniaはReactiveUIと親和性が高い
using Splat
を追加すれば、すぐにDI機能が使える
しかしながら、ReactiveUIのDI機能自体については説明があるものの、それをAvaloniaとどう組み合わせるかについては言及されていない
Dependency Injection - ReactiveUI
とくに登録についてはApp
クラスの親クラスが持っているpublic virtual void RegisterServices();
をつかってみたが、これが正攻法である保証はない
登録_Registration
App.xaml.cs
にあるApp
クラスのメソッドをオーバーライドする
別途Models
フォルダーにPerson
クラスとそのインターフェイスを用意し、このメソッド内で登録する
2つは名前付きのインスタンス、残り1つはシングルトンとして登録する
// add references
using Splat;
using AvaloniaApp.Models;
public class App : Application
{
// override method
public override void RegisterServices()
{
Locator.CurrentMutable.Register<IPerson>(() => new Person("Alice"), "a");
Locator.CurrentMutable.Register<IPerson>(() => new Person("Bob"), "b");
Locator.CurrentMutable.RegisterConstant<IPerson>(new Person("Carol"));
base.RegisterServices();
}
}
名前解決_Resolution
AliceとBobは呼ばれるたびにインスタンスが生成される
一方でCarolは登録時にインスタンスが生成されている
created at
の時刻をみても、Carolの方が早い時間に生成されていることがわかる
RegisterLazySingleton
というメソッドもあるが、これは呼ばれたときにインスタンスが生成されるタイプ
引数がデリゲートであるか否かで、インスタンスが生成されるタイミングを類推できる
using Splat;
public MainWindowViewModel()
{
var a = Locator.Current.GetService<IPerson>("a");
var b = Locator.Current.GetService<IPerson>("b");
var c = Locator.Current.GetService<IPerson>();
a.Greeting();
b.Greeting();
c.Greeting();
}
/*
I am Alice, created at 20:21:09
I am Bob, created at 20:21:09
I am Carol, created at 20:21:03
*/
インスタンスの寿命
-
Register
で登録したインスタンスは、生成されたスコープ内で同一 -
Constant
で登録したインスタンスは、どこで呼ばれても同一
public MainWindowViewModel()
{
var a = Locator.Current.GetService<IPerson>("a");
var c = Locator.Current.GetService<IPerson>();
a.Greeting(); // 1
c.Greeting(); // 2
GreetingTemp();
}
private void GreetingTemp()
{
Thread.Sleep(2000);
var a = Locator.Current.GetService<IPerson>("a");
var c = Locator.Current.GetService<IPerson>();
a.Greeting(); // 3
c.Greeting(); // 4
}
/*
I am Alice, created at 3:48:41
I am Carol, created at 3:48:39
I am Alice, created at 3:48:43
I am Carol, created at 3:48:39
*/
登録時の注意
あとに登録したインスタンスが優先されるため、下記の場合Carol
を呼び出す術がなくなる
// Registration
Locator.CurrentMutable.RegisterConstant<IPerson>(new Person("Carol"));
Locator.CurrentMutable.RegisterConstant<IPerson>(new Person("CarolBrother"));
// Resolution
var c = Locator.Current.GetService<IPerson>(); // <- CarolBrother
おわりに
AvaloniaのMVVMテンプレートをもとに、Dependency Injectionを試した
App
が継承しているメソッドをオーバーライドするとおさまりがよさそう
※ 引数なしの関数なので、override RegisterService
に入れなきゃいけないわけではない
Discussion