0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ReactiveCollectionの参照差し替えに要注意

Last updated at Posted at 2025-06-01

ある日の業務中のこと…

WPFアプリの開発中の出来事でした

他のクラスから受けとった値でReactiveCollection型のプロパティを更新

Subscribeしているクラスで変更後の処理を実行

という流れを実装しようとしていましたのですが、
なんとサブスクライブした後の処理が実行されないのです🤔

以下のような実装になっていました(必要なusingの記載は省略しています)

// 値の管理を行うクラス
public class IntsService
{
    // 整数値のコレクション(管理したい値)
    public ReactiveCollection<int> Ints { get; set; } = [];
}
// 変更をサブスクライブする側のクラス
public class IntsSubscriber
{
    private readonly IntsService intsService;

    public IntsSubscriber()
    {
        this.intsService = new IntsService();

        // 値の追加をサブスクライブ
        // 本来はコンソール出力ではなく値変更後に実行したいメソッドを渡してました
        this.intsService.Ints
            .ObserveAddChanged()
            .Subscribe(x => Console.WriteLine("値が追加されました"));
    }

    public void ChangeProperty()
    {
        // ReactiveCollectionの値を変更
        this.intsService.Ints = new ReactiveCollection<int>()
            .AddRange(new List<int> { 1, 2, 3 });
    }
}
public static class ReactiveCollectionExtensions
{
    // ReactiveCollectionではListのAddRangeが使えないため自前の拡張メソッドで実現
    // 本題とは関係ないです
    public static ReactiveCollection<T> AddRange<T>(this ReactiveCollection<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
        return collection;
    }
}

なぜ変更通知が飛ばなかったのか

皆さんはお分かりでしょうか?

原因はIntsSubscriberChangePropertyメソッドにありました

public void ChangeProperty()
{
    // ReactiveCollectionの値を変更
    this.intsService.Ints = new ReactiveCollection<int>()
        .AddRange(new List<int> { 1, 2, 3 });
}

ここで値を変更するためにReactiveCollectionをnewしているんですね
つまり、参照を書き換えてしまっているわけです

ReactiveCollectionは.NET標準のObservableCollectionを継承しており、
変更通知の仕様はCollectionChangedメソッドと同様になります

CollectionChangedの仕様を確認してみると以下の記載があります

項目が追加、削除、または移動されたとき、またはリスト全体が更新されたときに発生します。

同じ参照のコレクションに対する変更は通知されますが、
参照を書き換えてしまうとイベントが発火されないのです

じゃあなんで単純に値をAddする実装にしていなかったのかという話ですが、
直近で触っていたFlutterのプロパティ変更通知がそういう仕様になっていたからですね

ReactiveCollectionも同じ仕様だろうと勘違いをして、
こんなお粗末な実装をしていたわけです😩

解決方法

参照を変えずに値を変更すれば解決です

public void ChangeProperty()
{
    // ReactiveCollectionの値を変更
    this.intsService.Ints.AddRange(new List<int> { 1, 2, 3 });
}

参照差し替えには要注意です!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?