C# Tips | コレクション・LINQ:ObservableCollection変換

C# C#
スポンサーリンク

はじめに:「ObservableCollection変換」は“画面とコレクションをつなぐ橋”

業務アプリ(特に WPF / MAUI / WinUI など)を書くと、
List<T>IEnumerable<T> でデータを持っているのに、
画面バインディングでは ObservableCollection<T> が欲しくなる、という場面が必ず出てきます。

LINQ でデータを加工したあと、その結果をそのまま画面に出したい
検索結果やフィルタ結果を、画面のリストに反映したい
マスタデータを読み込んで、画面側のコレクションに流し込みたい

こういうときに使うのが「ObservableCollection変換」です。
つまり、

IEnumerable<T> / List<T>ObservableCollection<T>

への変換を、ユーティリティとしてサッと書けるようにしておく、という話です。

ここから、

ObservableCollection がなぜ必要なのか
LINQ 結果を ObservableCollection に変換する基本パターン
拡張メソッドとして ToObservableCollection を用意する
実務での使いどころ(検索結果・フィルタ結果・マスタ読み込み)

を、初心者向けにかみ砕いて説明していきます。


ObservableCollection とは何か、なぜ必要なのか

List との一番大きな違い:「変化を通知してくれる」

ObservableCollection<T> は、ざっくり言うと「通知機能付きの List」です。

List<T>
要素の追加・削除はできるが、「変わったよ」とは誰にも教えない

ObservableCollection<T>
要素の追加・削除があると、「変わったよ」というイベントを発行してくれる

WPF や MAUI などのデータバインディングは、この「変わったよ通知」を使って画面を自動更新しています。

画面にバインドするコレクションが List<T> だと、
中身を変えても画面は勝手には更新されません。
ObservableCollection<T> なら、要素を追加・削除した瞬間に画面が追従してくれます。

ここでの重要ポイントは、「画面にバインドするコレクションは、基本的に ObservableCollection<T> にしておくと幸せになりやすい」ということです。


LINQ 結果を ObservableCollection に変換する基本

まずは素朴な書き方から

例えば、ユーザー一覧を LINQ で絞り込んだあと、その結果を画面に出したいとします。

using System;
using System.Collections.ObjectModel;
using System.Linq;

public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
}

var allUsers = new[]
{
    new User { Id = 1, Name = "Alice" },
    new User { Id = 2, Name = "Bob" },
    new User { Id = 3, Name = "Charlie" },
};
C#

「名前に a を含むユーザーだけを画面に表示したい」という場合、LINQ でこう絞ります。

var filtered = allUsers
    .Where(x => x.Name.Contains("a", StringComparison.OrdinalIgnoreCase));
C#

この filteredIEnumerable<User> です。
これを ObservableCollection<User> に変換するには、こう書けます。

var observable = new ObservableCollection<User>(filtered);
C#

これで、observable を画面にバインドすれば、
「LINQ で絞り込んだ結果」がそのまま画面に出せます。

ここでの重要ポイントは、「ObservableCollection<T> はコンストラクタで IEnumerable<T> を受け取れる」ということです。
つまり、「LINQ の結果 → そのまま渡して ObservableCollection にする」という流れが自然に書けます。


ユーティリティ拡張メソッド ToObservableCollection を作る

毎回 new ObservableCollection<T>(…) と書くのは少しダルい

LINQ のチェーンの最後に、毎回こう書くのは少し長いです。

var observable = new ObservableCollection<User>(
    allUsers.Where(x => 条件)
);
C#

そこで、「ToList() と同じノリで ToObservableCollection() が欲しい」という気持ちになります。
自分で拡張メソッドを 1 本用意しておきましょう。

using System.Collections.Generic;
using System.Collections.ObjectModel;

public static class ObservableCollectionExtensions
{
    public static ObservableCollection<T> ToObservableCollection<T>(
        this IEnumerable<T> source)
    {
        return new ObservableCollection<T>(source);
    }
}
C#

使い方はこうです。

var observable = allUsers
    .Where(x => x.Name.Contains("a", StringComparison.OrdinalIgnoreCase))
    .ToObservableCollection();
C#

ここでの重要ポイントは、「ToObservableCollection() という名前だけで、“画面バインド用のコレクションにしている”ことが一目で分かる」ということです。
ToList() と同じ感覚で使えるので、チーム内でもすぐに浸透します。


実務での使いどころ 1:検索結果を画面に出す

検索条件に応じて ObservableCollection を差し替える

例えば、WPF の ViewModel で、こういうプロパティを持っているとします。

public ObservableCollection<User> Users { get; } 
    = new ObservableCollection<User>();
C#

検索ボタンを押したときに、LINQ で絞り込んで Users を差し替えたい。
そんなとき、こう書きがちです。

void Search(string keyword)
{
    Users.Clear();

    var filtered = allUsers
        .Where(x => x.Name.Contains(keyword, StringComparison.OrdinalIgnoreCase));

    foreach (var u in filtered)
    {
        Users.Add(u);
    }
}
C#

これはこれでアリですが、ToObservableCollection を使うと、もう少しスッキリ書けます。

void Search(string keyword)
{
    var filtered = allUsers
        .Where(x => x.Name.Contains(keyword, StringComparison.OrdinalIgnoreCase))
        .ToObservableCollection();

    Users = filtered; // プロパティを set できる形にしておく
}
C#

あるいは、Users を set できない設計なら、
一時的に ObservableCollection を作ってから中身を入れ替える、という手もあります。

void Search(string keyword)
{
    var filtered = allUsers
        .Where(x => x.Name.Contains(keyword, StringComparison.OrdinalIgnoreCase))
        .ToObservableCollection();

    Users.Clear();
    foreach (var u in filtered)
    {
        Users.Add(u);
    }
}
C#

ここでの重要ポイントは、「LINQ で作った“新しい結果セット”を、ObservableCollection として一度に扱える」ということです。
ToObservableCollection があると、「LINQ の世界」と「画面バインドの世界」の橋渡しがとても楽になります。


実務での使いどころ 2:マスタ読み込み → 画面バインド

起動時にマスタを読み込んで、そのまま画面に出す

例えば、起動時に DB から商品マスタを読み込んで、画面のリストに出したいケース。

public ObservableCollection<Item> Items { get; }
    = new ObservableCollection<Item>();

void Load()
{
    var allItems = _itemRepository.GetAll(); // 戻り値は IEnumerable<Item>

    var observable = allItems
        .OrderBy(x => x.Code)
        .ToObservableCollection();

    Items.Clear();
    foreach (var item in observable)
    {
        Items.Add(item);
    }
}
C#

ここでの重要ポイントは、「DB から取ってきた IEnumerable<T> に対しても、LINQ で加工してから ToObservableCollection すれば、そのまま画面に流し込める」ということです。
「データ取得 → LINQ で整形 → ObservableCollection に変換 → 画面へ」というパイプラインが、きれいに一本線で書けます。


実務での使いどころ 3:別型への変換+ObservableCollection

DTO や ViewModel に変換してからバインドしたい

業務では、「DB のエンティティそのもの」ではなく、
画面用の ViewModel や DTO に変換してからバインドしたいことが多いです。

public class UserViewModel
{
    public int Id { get; set; }
    public string DisplayName { get; set; } = "";
}
C#
var userViewModels = allUsers
    .Select(u => new UserViewModel
    {
        Id = u.Id,
        DisplayName = $"{u.Id}: {u.Name}"
    })
    .ToObservableCollection();
C#

ここでの重要ポイントは、「Select で別型に変換した結果にも、そのまま ToObservableCollection をつなげられる」ということです。
LINQ の変換と ObservableCollection 変換を、自然なチェーンとして書けます。


ObservableCollection変換で気をつけたいこと

「変換したあとに追加・削除するかどうか」を意識する

ToObservableCollection で変換したコレクションは、もちろんあとから Add / Remove できます。
ただし、

一度きりの表示用(検索結果を出して終わり)
その後は中身を変えない

という用途なら、ToList() でも足りる場面もあります。

逆に、

画面上で行の追加・削除をさせたい
別の処理からコレクションを更新したい

といった場合は、ObservableCollection にしておく意味が大きくなります。

ここでの重要ポイントは、「“本当に画面と一緒に動的に変わる必要があるか”を考えてから ObservableCollection にする」ということです。
なんでもかんでも ObservableCollection にすると、逆に設計が重くなることもあります。


まとめ:「ObservableCollection変換ユーティリティ」は“LINQ と画面をつなぐ変換アダプタ”

ObservableCollection変換の本質は、

LINQ で気持ちよくデータを加工したあと、
その結果をそのまま画面にバインドできる形に変える

ことです。

押さえておきたいポイントを整理すると、

画面バインドには ObservableCollection<T> が向いている(変更通知が飛ぶ)
new ObservableCollection<T>(IEnumerable<T>) で LINQ 結果をそのまま変換できる
ToObservableCollection() 拡張メソッドを 1 本用意しておくと、チェーンの最後に自然に書ける
検索結果・マスタ読み込み・DTO/ViewModel 変換など、業務での出番はかなり多い
「本当に動的更新が必要なところ」にだけ ObservableCollection を使う、というメリハリも大事

ここまで腹落ちしていれば、
「LINQ で作った結果をどう画面に渡すか」で迷う時間が減って、
“データの流れを意識して設計できる C# エンジニア”に一歩近づけます。

タイトルとURLをコピーしました