ObservePropertyChanged
1. 概要
ObservePropertyChanged は、INotifyPropertyChanged を実装したオブジェクトのプロパティ変更をイベント駆動(プッシュ型)で監視する拡張メソッドです。
ネストしたプロパティパスを最大 3 階層まで指定でき(例: x => x.Child → c => c.Name)、中間オブジェクトが変わると自動的に監視先が切り替わります。
既定では購読時に現在の値を即座に発行します(pushCurrentValueOnSubscribe: true)。
注意: これは拡張メソッドです。
source.ObservePropertyChanged(...)の形式で呼び出します。
2. シグネチャ
1 階層(直接プロパティ)
public static Observable<TProperty> ObservePropertyChanged<T, TProperty>(
this T value,
Func<T, TProperty> propertySelector,
bool pushCurrentValueOnSubscribe = true,
CancellationToken cancellationToken = default,
[CallerArgumentExpression(nameof(propertySelector))] string? expr = null)
where T : INotifyPropertyChanged対象オブジェクトの直接プロパティを監視します。propertySelector にはシンプルなプロパティアクセス式(例: x => x.Name)を指定します。プロパティ名は CallerArgumentExpression により自動的に抽出されます。
viewModel.ObservePropertyChanged(x => x.Name)2 階層(ネストプロパティ)
public static Observable<TProperty2> ObservePropertyChanged<T, TProperty1, TProperty2>(
this T value,
Func<T, TProperty1?> propertySelector1,
Func<TProperty1, TProperty2> propertySelector2,
bool pushCurrentValueOnSubscribe = true,
CancellationToken cancellationToken = default,
...)
where T : INotifyPropertyChanged
where TProperty1 : INotifyPropertyChanged2 階層のネストしたプロパティを監視します。中間プロパティ TProperty1 も INotifyPropertyChanged を実装している必要があります。中間オブジェクトが null に変わった場合は、発行が停止します。
viewModel.ObservePropertyChanged(
x => x.SelectedItem,
item => item.Name)3 階層(深いネストプロパティ)
public static Observable<TProperty3> ObservePropertyChanged<T, TProperty1, TProperty2, TProperty3>(
this T value,
Func<T, TProperty1?> propertySelector1,
Func<TProperty1, TProperty2?> propertySelector2,
Func<TProperty2, TProperty3> propertySelector3,
bool pushCurrentValueOnSubscribe = true,
CancellationToken cancellationToken = default,
...)
where T : INotifyPropertyChanged
where TProperty1 : INotifyPropertyChanged
where TProperty2 : INotifyPropertyChanged3 階層までネストしたプロパティを監視します。すべての中間プロパティが INotifyPropertyChanged を実装している必要があります。
viewModel.ObservePropertyChanged(
x => x.Department,
d => d.Manager,
m => m.Name)主なパラメータ
| パラメータ | 説明 |
|---|---|
propertySelector | 監視するプロパティを指定するラムダ式。シンプルなプロパティアクセス(x => x.Prop)である必要がある |
pushCurrentValueOnSubscribe | true(既定値)の場合、購読時に現在の値を即座に発行する |
cancellationToken | キャンセル時に監視を停止する |
3. マーブルダイアグラム
購読時に pushCurrentValueOnSubscribe が true であれば現在の値が即座に発行されます。その後、PropertyChanged イベントが発火するたびに最新の値が発行されます。
4. サンプルコード
using R3;
using System.ComponentModel;
// === ViewModel の定義 ===
class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private string _name = "";
public string Name
{
get => _name;
set
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
private int _age;
public int Age
{
get => _age;
set
{
_age = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Age)));
}
}
}
var vm = new MyViewModel { Name = "太郎", Age = 25 };
// === 1 階層: 直接プロパティの監視 ===
vm.ObservePropertyChanged(x => x.Name)
.Subscribe(name => Console.WriteLine($"名前: {name}"));
// 出力(即座): 名前: 太郎
vm.Name = "花子";
// 出力: 名前: 花子
// === 初回値を発行しない ===
vm.ObservePropertyChanged(x => x.Age, pushCurrentValueOnSubscribe: false)
.Subscribe(age => Console.WriteLine($"年齢: {age}"));
// この時点では出力なし
vm.Age = 30;
// 出力: 年齢: 30
// === 2 階層: ネストプロパティの監視 ===
// viewModel.ObservePropertyChanged(
// x => x.SelectedItem,
// item => item.Description)
// .Subscribe(desc => Console.WriteLine($"説明: {desc}"));
// SelectedItem が変わると、新しい SelectedItem の Description の監視に自動切り替え5. 補足
EveryValueChanged との違い
EveryValueChanged はポーリングベース(プル型)で、毎フレーム値を比較して変化を検知します。INotifyPropertyChanged の実装が不要なため、任意のクラスに使用できますが、フレームごとの比較コストが発生します。
ObservePropertyChanged はイベント駆動(プッシュ型)のため、プロパティが変更されたときだけ処理が実行されます。INotifyPropertyChanged を実装した ViewModel での使用に適しています。
ObservePropertyChanging との違い
ObservePropertyChanging は INotifyPropertyChanging インターフェースを使用し、プロパティが変更される前に通知されます。変更前の値(現在の値)を取得したい場合に使用します。