sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

C# CommunityToolkit.Mvvm 8.2 の確認

.NET Community Toolkit.Mvvm の記事を10回に分けて更新してきましたが、今回は .NET Blog に掲載された「Announcing .NET Community Toolkit 8.2! Faster generators, code fixers, performance improvements and more!」の内容をメモしました。

和訳は、ざっくりです。DeepL なども使っていますが全部ではないので、間違いもあると思います。楽したくて「読点 "、"」 を「句点 "。"」によく置きかえて訳しています。

.NET Community Toolkit 8.2!

.NET Community Toolkit 8.2 のリリースについて ooficial launch を発表できることをうれしく思います!

新しいバージョンは、MVVM Toolkit のソースジェネレータ、および、runtime の両方のパフォーマンス改善、生産性を向上させるための新しい code fixer、ユーザーから提案のあった新しい機能などを含んでいます。

.NET Community Toolkit をより良いものにするために助けてくれるみんなに感謝します!🎉

.NET Community Toolkit には何がありますか?👀 (What’s in the .NET Community Toolkit? 👀)

.NET Community Toolkit には、以下のライブラリが含まれています:

  • CommunityToolkit.Common
  • CommunityToolkit.Mvvm (also known as “Microsoft MVVM Toolkit”)
  • CommunityToolkit.Diagnostics
  • CommunityToolkit.HighPerformance

これらのコンポーネントは、Microsoft Store や Photo App など Windows に同梱されているいくつかのアプリでもよく使われています。🚀

.NET Community Toolkit の歴史の詳細については、以前の 8.0.0 の「記事」をご覧ください。

以下は、.NET Community Toolkit 8.2 の新しいリリースに含まれている主な変更点についてのかみ砕いた内容 (break down) です。

Custom attributes [RelayCommand] 🤖

MVVM Toolkit 8.1.0 リリースで行わた作業をフォローアップして、MVVM Toolkit 8.2.0 では(GitHub で提案のあった)RelayCommand を使用する際にカスタム属性のサポートが含まれます。

C# の構文を活かして field:***property:*** といった、カスタム属性のターゲットを示します。これによって、RelayCommand を使用して MVVM command を生成する際に、生成されるすべてのメンバーの属性を制御できるようになりました。

例えば、JSON シリアライズをサポートする必要のある viewmodelを使用しているとします。生成されるプロパティを明示的に無視する (ignore) 場合、特に便利です。

次のようにサポートできます:

[RelayCommand]
[property: JsonIgnore]
private void DoWork()
{
    // Do some work here...
}

後ろでは、次のようになっています:

private RelayCommand? _doWorkCommand;

[JsonIgnore]
public IRelayCommand DoWorkCommand => _doWorkCommand ??= new RelayCommand(DoWork);

生成された DoWorkCommand プロパティは、(予想通りの)属性を持っています! もちろん、コンストラクタや名前付きパラメーターを持つ属性もサポートします。加えて、field:***property:*** 属性の2つを組み合わせて使うこともできます。🙌

新しい source generator に関するドキュメントはすべて「ここ」で見ることができます。また、ビデオがいいなら James Montemagno が「いくつか」作っています。

新しい ObservableProperty の変更フック⚗️

MVVM で比較的一般的なシナリオは、例えば、(現在選択されているユーザーやネストされた viewmodel を表すような)"selected item" の observable プロパティを持つことです。

プロパティの値が変更されたとき、古いインスタンスと新しいインスタンスに対して、何らかの調整をしなければならないことは珍しいことではありません。例えば "selected" プロパティの設定やイベントの購読などがそうです。

以前の場合では、ObservableProperty を使用するのは、理想的とはいえないシナリオでした。なぜなら、設定されている新/旧の値に対して必要な状態変更をするための、このようなロジックを楽に挿入するために必要な機能を持っていなかったためです。

この(持っていない状態を)修正するために、MVVM Toolkit 8.2 のリリースから、すべての ObservableProperty のフィールドに対して、生成される2つの新しいプロパティの変更フックがあります。

例えば、次のようなコードを考えてみます:

[ObservableProperty]
private DocumentViewModel _selectedDocument;

これは、次のようなコードが生成されます:

public DocumentViewModel SelectedDocument
{
    get => _selectedDocument;
    set
    {
        if (!EqualityComparer<DocumentViewModel>.Default.Equals(_selectedDocument, value))
        {
            DocumentViewModel? oldValue = _selectedDocument;
            OnNameChanging(value);
            OnNameChanging(oldValue, value);
            OnPropertyChanging();
            _selectedDocument = value;
            OnNameChanged(value);
            OnNameChanged(oldValue, value);
            OnPropertyChanged();
        }
    }
}

partial void OnSelectedDocumentChanging(DocumentViewModel value);
partial void OnSelectedDocumentChanged(DocumentViewModel value);

partial void OnSelectedDocumentChanging(DocumentViewModel? oldValue, DocumentViewModel newValue);
partial void OnSelectedDocumentChanged(DocumentViewModel? oldValue, DocumentViewModel newValue);

2つの新しい "OnPropertyNameChaning" メソッドと "Changed" メソッドが生成されていることに注目ください。

2つは、コードを挿入するために使いやすいフックを提供しています。各プロパティの変更イベントでトリガーされて、設定されている新/旧の両方の値を変更することができます。

例えば、以下のように使用できます:

partial void OnSelectedDocumentChanging(DocumentViewModel? oldValue, DocumentViewModel newValue)
{
    if (oldValue is not null)
    {
        oldValue.IsSelected = false;
    }

    newValue.IsSelected = true;
}

必要なことはこれだけです! viewmodel から OnSelectedDocumentChanging の呼び出しがされます。ObservableProperty には、このサポート機能が組み込まれています。(partial に注意)

Note: MVVM Toolkit は、コードジェネレーターだけで最適化をするために、上記のメソッドを使用しているかどうかを自動的に検出しています。(実際のコード上に利用されていない)実装されていない(生成予定だった)メソッドの呼び出しはコンパイラによって削除されます。なので、この機能は完全に pay for play です!(使うときにだけ支払う、のような意味だと思います)

MVVM Toolkit の Code Fixer (MVVM Toolkit code fixers 📖)

MVVM Toolkit の以前のリリースで、2つの新しい診断アナライザーを追加しました。 これは、ObservableProperty でマークされたフィールドに誤ってアクセスしたとき、または、ObservableProperty と同様の属性を持つ型を宣言したときに、(継承可能なときは)警告を生成します。

8.2 のリリースでは、これら2つのアナライザーに built-in の code fixer が追加されます。

警告を出すたびに、インテリセンスの電球にカーソルを合わせてコード修正を選択すれば、コードを正しい形に戻すための変更作業を自動的に適用できるようになりました! また、一括修正もサポートしているので、ワンクリックでエラーを修正することもできます!✨

[ObservableProperty]
private string? _name;

[RelayCommand]
private void ResetName()
{
    // 修正前 (警告が出る)
    _name = null;

    // インテリセンスによる指摘で修正できる
    Name = null;
}

この機能では Visual Studio の新しい code fixer の UI を確認しました。変更のプレビューやターゲットスコープに修正を適用するオプションが表示される、といった内容でした。

MVVM Toolkit ソース・ジェネレーターの最適化 (MVVM Toolkit source generator optimizations 🛫)

毎回のリリースと同じように、MVVM Toolkit 8.2 でもソースジェネレーターのパフォーマンス改善が含まれています。

今回は、incremental pipelines を最適化してメモリ使用量を最小化したこと、同時実行時に不要なオブジェクトは永続しないようにすることにフォーカスしました。改善するために行われた内容 PR をいくつか紹介します。

  • Move remaining diagnostics to analyzers (#581): MVVM Toolkit の診断機能が(プロセス外で同時実行できる)diagnostic analyzer に移動した。これにより incremental pipelines からいくつか Roslyn シンボルが削除されて、パフォーマンスが向上。

  • Resolve symbols early in analyzers (#587): 必要なアナライザーのシンボルはすべて、最初のコールバックのセットアップ中に解決されるようになった。各コンパイルインスタンスでのコールバック実行が高速化した。

Other changes and improvements🚀

略。

リリースの全変更履歴は「ここ」。

今日からはじめろ! (Get started today! 🎉)

すべてのソースコードGitHub に、ドキュメントは MS Learn に、完全な API リファレンスは .NET API ブラウザの web サイトにある。

Happy coding! 💻

参考

レガシーコードとどう付き合うか

レガシーコードとどう付き合うか

Amazon

この記事はすべて公開されています。この記事が「よかった」という方は、記事更新のためのサポートをお願いします。🚀