bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] System.Collections.ObjectModel. ObservableCollection<T> — 変更通知付きコレクションの仕組みと使いどころ

[C#] System.Collections.ObjectModel. ObservableCollection<T> — 変更通知付きコレクションの仕組みと使いどころ

Jun 1, 2026 C# , .NET bucket-sort

System.Collections.ObjectModel.ObservableCollection<T> は、要素の追加・削除・変更を外部に通知する 機能を持ったコレクションです。WPF、MAUI、Avalonia などのデータバインディングフレームワークと組み合わせ、コレクションの変更が UI に自動反映される MVVM パターンの主役として使われます。

ObservableCollection<T> とは

  • 名前空間:System.Collections.ObjectModel
  • 基底クラス:Collection<T>(List<T> を内部に持つラッパ)
  • 要素変更時に CollectionChanged イベントを発火
  • Count などのプロパティ変更時に PropertyChanged イベントを発火
  • 内部構造は List<T> と同じ動的配列
using System.Collections.ObjectModel;
using System.Collections.Specialized;

var items = new ObservableCollection<string> { "apple", "banana" };

items.CollectionChanged += (s, e) =>
{
    Console.WriteLine($"Action: {e.Action}");
    if (e.NewItems != null)
        foreach (var x in e.NewItems) Console.WriteLine($"  + {x}");
    if (e.OldItems != null)
        foreach (var x in e.OldItems) Console.WriteLine($"  - {x}");
};

items.Add("cherry");
// Action: Add
//   + cherry

items.Remove("apple");
// Action: Remove
//   - apple

items[0] = "blueberry";
// Action: Replace
//   - banana
//   + blueberry

items.Move(0, 1);
// Action: Move

サポートするインターフェース

インターフェース 役割
IList<T> インデックスアクセス・挿入・削除を持つ順序付きコレクション
IReadOnlyList<T> 読み取り専用ビュー
ICollection<T> Count / Add / Remove 等
IReadOnlyCollection<T> 読み取り専用 Count
IEnumerable<T> foreach での列挙
IList / ICollection / IEnumerable 非ジェネリック互換
INotifyCollectionChanged コレクション変更通知
INotifyPropertyChanged プロパティ変更通知(Count、Item[])

IList<T> などの基本コレクション系インターフェースの詳細は List の記事 を、IEnumerable は IEnumerable と IEnumerator の記事 を参照してください。

INotifyCollectionChanged

ObservableCollection<T> の中核となる、コレクションの内容変更を通知 するためのインターフェース。

public interface INotifyCollectionChanged
{
    event NotifyCollectionChangedEventHandler? CollectionChanged;
}

イベント引数 NotifyCollectionChangedEventArgs には Action(変更種別)、OldItems、NewItems、OldStartingIndex、NewStartingIndex が含まれます。

Action は NotifyCollectionChangedAction 列挙体:

値 意味
Add 新規要素が追加された
Remove 要素が削除された
Replace インデクサ代入で要素が置換された
Move Move(oldIndex, newIndex) で並び替えられた
Reset Clear で全削除された

INotifyPropertyChanged

Count プロパティや Item[](インデクサ)が変わったことを通知。WPF などの UI が Count をテキストバインドしているケースで効きます。

public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler? PropertyChanged;
}

主な API と計算量

性能特性は List<T> とほぼ同じ(内部に List<T> を持つため)。加えて変更時にイベント発火のオーバーヘッドが乗ります。

操作 API 計算量 備考
インデックスアクセス [i] O(1) 通知なし(ゲッタは)
末尾追加 Add(item) O(1) 償却 Add 通知
任意位置挿入 Insert(i, item) O(n) Add 通知
削除 Remove(item) / RemoveAt(i) O(n) Remove 通知
置換 [i] = item O(1) Replace 通知
並び替え Move(oldIndex, newIndex) O(n) Move 通知
全削除 Clear() O(n) Reset 通知

イベントハンドラの処理が重いと UI が固まるので、ハンドラ側の処理は軽く することが鉄則。

WPF / MAUI / Avalonia でのバインディング

XAML 側で ItemsControl.ItemsSource などにバインドすると、コレクションの変更が 自動的に UI に反映 されます。

<ListBox ItemsSource="{Binding Items}" />
public class MainViewModel
{
    public ObservableCollection<string> Items { get; } = new();
}

Items.Add(...) するだけでリストの末尾に新しい行が追加されます。List<T> ではこの自動更新が効きません(変更通知を発火する仕組みがない)。

UI スレッド制約

WPF/MAUI などの UI フレームワークは、UI スレッドからのみ ObservableCollection を変更する ことを期待します。バックグラウンドスレッドから Add を呼ぶと NotSupportedException や InvalidOperationException が発生することがあります。

対処は次のいずれか。

  1. UI スレッドにマーシャル

    // WPF
    Application.Current.Dispatcher.Invoke(() => items.Add(x));
    // MAUI
    MainThread.BeginInvokeOnMainThread(() => items.Add(x));
    
  2. BindingOperations.EnableCollectionSynchronization(WPF 専用)

    var sync = new object();
    BindingOperations.EnableCollectionSynchronization(items, sync);
    // 以降、別スレッドからでも lock(sync) 内で変更すれば OK
    
  3. 専用ライブラリの活用:CommunityToolkit.Mvvm の ObservableCollection 拡張や、各種の thread-safe な observable コレクション。

List<T> との比較

観点 List<T> ObservableCollection<T>
通知 なし CollectionChanged / PropertyChanged
バインディング 静的(追加が UI に反映されない) 動的(自動反映)
パフォーマンス 高速 イベント発火分だけ遅い
AddRange あり なし(個別 Add)
用途 一般的な順序付き集合 バインディング対象

ObservableCollection<T> には AddRange がありません。多数の要素を一気に追加するとそのたびに通知が飛ぶため UI が遅くなります。バルク追加が必要なときは、

  • 新しい ObservableCollection<T> を作って差し替える
  • Reset 通知を 1 度だけ飛ばすカスタム派生クラスを作る
  • RangeObservableCollection 系のサードパーティ実装を使う

といった対処が一般的です。

派生で挙動を拡張する

Collection<T> を継承しているため、InsertItem / RemoveItem / SetItem / ClearItems をオーバーライドして 追加・削除・置換時の追加処理 を挟めます。

public class TrackedCollection<T> : ObservableCollection<T>
{
    protected override void InsertItem(int index, T item)
    {
        // 検証など
        base.InsertItem(index, item);
        // ログ記録など
    }
}

これは INotifyCollectionChanged のイベントを横から捕まえるよりクリーンな書き方です。

使いどころ

  1. WPF / MAUI / Avalonia / WinUI3 の MVVM:ViewModel が公開するリストの実装として事実上の標準。
  2. UI に表示するリアルタイムデータ:チャットの新着メッセージ、株価ティック、ログビューア など。
  3. 動的に変わる選択肢:ドロップダウンやリストボックスの項目を実行時に増減させるケース。

向かないケース

  • UI と無関係なデータ集合 → List<T> で十分。ObservableCollection<T> のオーバーヘッドは無駄。
  • 大量データのバルク変更 → 個別通知でパフォーマンスが悪化する。バルク対応の派生型を使う。
  • 並行アクセスが必須 → 標準では非スレッドセーフ。EnableCollectionSynchronization などで補強する。

注意点

  • イベントハンドラ内でコレクションを変更しない:再入で挙動が崩れる。
  • 解除しないハンドラはメモリリークの元:購読側が長命オブジェクトなら明示的に -= する。
  • AddRange 相当はない:パフォーマンス重視ならカスタム派生か差し替えで対応。

まとめ

  • ObservableCollection<T> は 変更通知付きコレクション。MVVM とデータバインディングの主役。
  • IList<T> / ICollection<T> / IEnumerable<T> に加え、INotifyCollectionChanged / INotifyPropertyChanged を実装。
  • 性能特性は List<T> ベース。イベント発火と UI スレッド制約を理解して使う。
  • 公開時は読み取り専用ビューが欲しいことも多い。次回は ReadOnlyObservableCollection<T> を扱う。
C# .NET ObservableCollection System.Collections.ObjectModel WPF MVVM データバインディング
← [C#] System.Collections.Generic.Stack<T> — 型安全な LIFO スタックの仕組みと使いどころ [C#] System.Collections.ObjectModel. ReadOnlyObservableCollection<T> — 変更通知付きの読み取り専用ビュー →

Related Posts

  • [C#] System.Collections.ObjectModel. ReadOnlyObservableCollection<T> — 変更通知付きの読み取り専用ビュー Jun 2, 2026
  • [C#] LiveChartsCoreで折れ線グラフを表示する(基本からレンジ切替まで) Jan 26, 2026
  • [C#] ジェネリックの型制約(Constraining Type Parameters)入門 — where T : struct / class / new() / 基底クラス / インターフェース Jun 3, 2026
  • [C#] System.Collections.Generic.Stack<T> — 型安全な LIFO スタックの仕組みと使いどころ May 31, 2026

Table of Contents

  • ObservableCollection<T> とは
  • サポートするインターフェース
    • INotifyCollectionChanged
    • INotifyPropertyChanged
  • 主な API と計算量
  • WPF / MAUI / Avalonia でのバインディング
  • UI スレッド制約
  • List<T> との比較
  • 派生で挙動を拡張する
  • 使いどころ
  • 向かないケース
  • 注意点
  • まとめ

Recent Posts

  • [C#] ジェネリックの型制約(Constraining Type Parameters)入門 — where T : struct / class / new() / 基底クラス / インターフェース Jun 3, 2026
  • [C#] System.Collections.ObjectModel. ReadOnlyObservableCollection<T> — 変更通知付きの読み取り専用ビュー Jun 2, 2026
  • [C#] System.Collections.ObjectModel. ObservableCollection<T> — 変更通知付きコレクションの仕組みと使いどころ Jun 1, 2026
  • [C#] System.Collections.Generic.Stack<T> — 型安全な LIFO スタックの仕組みと使いどころ May 31, 2026
  • [C#] System.Collections.Generic.SortedSet<T> — 赤黒木で実装される整列セット May 30, 2026

Categories

  • C#84
  • .NET83
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • System.Collections.Generic
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • DoS
  • Nginx
  • Windows
  • WordPress
  • メモリ管理
  • 監視
  • 設計
  • Amazon Linux 2023
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • OPCache
  • System.Collections.Specialized
  • Webサーバー
  • オブジェクト指向
  • クラス設計
  • デザインパターン
  • パターンマッチング
  • 継承
  • 認可
  • Aurora
  • Blade
  • Grafana
  • Hugo
  • InfluxDB
  • Policy
  • Record
  • SSG
  • WPF
  • インターフェース
  • エラーハンドリング
Powered by Hugo & Explore Theme.