WPF + MVVM パターンでアプリケーションを開発していると、Null チェックを行ったにも関わらず、その直後で Null 参照例外が発生する現象が発生しました。今回は、この問題の原因と解決策について説明します。
問題の発生状況
自作ファイラーの開発中に発生した事例です。ViewModel 内で以下のようなコードを実装していました。
// 問題のあるコード
if (SelectedTab?.SelectedItem != null)
{
// DoubleClickCommandと同じ動作を実行
SelectedTab.DoubleClickCommand?.Execute(SelectedTab.SelectedItem);
System.Diagnostics.Debug.WriteLine($"Enterキーで実行: {SelectedTab.SelectedItem.Name}");
// ↑この行でSelectedTab.SelectedItemがNullだと例外が発生
return true;
}
return false;
SelectedTab はデータバインディングされたプロパティで、if 文で Null チェックを行っているにも関わらず、その直後の処理で Null 参照例外が発生していました。
原因の分析
この問題の原因は、if 文の判定から実際の使用までの間に、別スレッドやイベントによってプロパティの値が変更されていることです。
特に MVVM パターンでは:
- データバインディングによる自動的なプロパティ更新
- ユーザーの操作による予期しないタイミングでの値変更
これらの要因により、Null チェック後であっても値が変更される可能性があります。
解決策
解決策 1: 根本原因の特定
プロパティを Null に変更している処理を特定し、適切なタイミングで実行されるよう修正します。
解決策 2: ローカル変数への退避(推奨)
プロパティの値をローカル変数に退避してから使用することで、処理中の値変更を防げます。
// 改善されたコード
var selectedTab = SelectedTab;
var selectedItem = selectedTab?.SelectedItem;
if (selectedTab == null)
{
return false;
}
if (selectedItem != null)
{
// DoubleClickCommandと同じ動作を実行
selectedTab.DoubleClickCommand?.Execute(selectedItem);
System.Diagnostics.Debug.WriteLine($"Enterキーで実行: {selectedItem.Name}");
return true;
}
return false;
改善のポイント
- 値の退避: プロパティアクセスを最小限に抑え、ローカル変数に値を保存
- 一貫性の確保: 処理全体で同じ値を使用することを保証
- 可読性の向上: 処理の意図が明確になる
まとめ
この問題は特にリアルタイムでデータが更新されるアプリケーションでよく発生するため、MVVM パターンを使用する際は常に意識しておくべき重要なポイントです。
この記事が皆様のコーディングライフの助けになれば幸いです。