はじめに
サラリーマンなプログラマです(自己紹介)。
2018年に仕事でWPF新規アプリ開発をやったので知見をまとめておきます。
- パッケージ開発
- 全体で50KStepくらいのWPFアプリをチームで新規開発
- 自分はリードエンジニアとしてOSSの選定から設計や実装までがっつりやらせてもらえました(幸せ
MVVM関係
そもそもMVVMで作るべきか
大抵の場合は作るべきです。作ってよかった。
作らなくていいのは今後絶対に保守しないことが分かっている場合と、規模がかなり小さい場合。ですが、未来のことは分からないので「あり得ない」なんてことはあり得ないような気がする。
MVVMに限らずなんとかパターンというのはほとんどの場合設計に冗長性を持たせて将来の変更要求に柔軟に対応するってことなんだと思いますが、将来の変更要求自体がない場合とか柔軟に対応せずに全パターン力技でテストできるくらい規模の小さいアプリならパターンを適用するために冗長性を実装する初期投資のデメリットの方が大きいかもしれません。
ViewModelはなるべく薄くする
- ViewModelに書く処理を可能な限り減らすこと
- ほとんどの場合ViewModelに書く処理はModelかViewに書くことができる
- なんとなく以下のことに気をつけたら上手くいった
- ViewModelでModelのメソッドの戻り値を受け取らない
- ViewModelでModelのメソッド呼び出しの例外をキャッチしない
- ViewModelでModelの非同期メソッドを待機しない
反省点として、可能な限り状態の保持や処理をModel側に追い出した結果、今度はModel側がアプリのステート(これは画面遷移をまたいでも維持しておきたい画面の状態とかも含む)と、アプリのステートとは無関係なビジネスロジックとで混沌としてしまったこと。
と言ってもPresentation側が混沌とする100倍はマシなので合格点は取れたと思ってます。
Model側も何層かに分けて、たとえばユースケース層とエンティティ層みたいな感じで分けて、アプリのステートの保持と加工、永続的な情報の保持と加工、ってデータの寿命ごとに責務を分割できればもっと見通しが良くなったかもしれない。
ReactiveXXXを活用する
VMの役割はModelのプロパティを(必要があれば変換して)Viewに公開すること。
そのためにはModelのプロパティの更新通知を受け取って自分のプロパティを同期する仕組みが必要ですが、真面目にイベント構文だけで作るとかなりしんどい(特にコレクションの同期)。
ReactivePropertyとかReactiveUIを使うとプロパティの同期をかなり簡潔に記述できます。自分はReactiveProperty使ったんですけどめっちゃ良かったです。
微妙に足りない部分はReactivePropertyのソースコード見ながらユーティリティ作って拡張していった。
バックグラウンドで処理しようとすると最初になんか固まる
ThreadPool.SetMinThreads(300, 300);
こんな感じでスレッドの最大数を設定してやればよい、というのを見つけたので試してみたらめっちゃキビキビ動くようになたのでこれ必須。
マシンにメモリたくさん積んでるのにOutOfMemory例外が出る
でかいオブジェクトを頻繁に作成と破棄する場合はメモリの断片化が起きてる可能性が高い。
LOHのコンパクションを有効にしてみると有効な場合あり。
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
また、純粋にメモリ使用量が多すぎる場合として、32bitアプリの場合、そもそもプロセスが2GBくらいしかメモリを使えない(そのうち1GBくらいをモジュールの読み込みとかで使うので実質1GBくらいしか使えない)という制約があって、そこに引っ掛かってる可能性あり。
ビルド後のexeファイルのバイナリを書き換えて4GBまで使えるようにする方法がある(LAAオプション)。
editbin.exe /largeaddressaware "【exeファイルのパス】"
ただ、これでも使えるのは4GBまでなので、根本的にはメモリ使用量を減らすか、AnyCPUとか64bitアプリとしてビルドする方がいい。
自分の場合は(政治的な理由で)どっちの修正方法もできなかった。いつか直す。
2019-11-18 追記
他に共有メモリに情報を退避させるという方法もありますね。共有メモリはプロセスのメモリ上限にひっかからないらしいです。
ただ、解放漏れがあるとマシン再起動まで続くメモリリークが発生すると思われるので、古い資産や環境をサポートしたいといった事情がない限り、新しく作るアプリは素直に64bitのみサポートとするのが楽だと思います。
Rx便利
ReactiveExtensions(あとReactiveProperty)、業務で使ったのはこれが初めてだったのですが予想以上に便利です。サンプルコードとかだとあんまり便利さが伝わらないかもですが、今までC#標準のイベントでがりがりやってたところをさっくり実装できるのでめちゃくちゃスッキリします。