C#におけるIEnumerable
とList
の違い
はじめに
C#でプログラミングを始めると、コレクション(データの集まり)を扱う機会が多くあります。その中でもよく使われるのがIEnumerable
とList
です。しかし、初学者の方には「いつどちらを使えばいいの?」「何が違うの?」という疑問があると思います。
この記事では、IEnumerable
とList
の違いを図解とサンプルコードを使って、わかりやすく説明します。
基本的な概念の違い
IEnumerable(アイイニューメラブル)とは
IEnumerable
はインターフェースです。「順番にデータを取り出せる」という機能を定義しています。
Listとは
List
は具体的なクラスです。実際にデータを格納し、様々な操作ができます。
関係性を図で理解しよう
実際のコード例で違いを見てみよう
IEnumerableの使用例
using System;
using System.Collections.Generic;
using System.Linq;
public class Example
{
// IEnumerableを返すメソッド
public static IEnumerable<int> GetNumbers()
{
yield return 1;
yield return 2;
yield return 3;
}
public static void Main()
{
IEnumerable<int> numbers = GetNumbers();
// foreach文で繰り返し処理
foreach (int num in numbers)
{
Console.WriteLine(num);
}
// LINQも使える
var evenNumbers = numbers.Where(x => x % 2 == 0);
Console.WriteLine("偶数: " + string.Join(", ", evenNumbers));
}
}
出力結果:
1
2
3
偶数: 2
Listの使用例
using System;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
// Listの作成と初期化
List<string> fruits = new List<string> { "りんご", "バナナ", "オレンジ" };
// 要素の追加
fruits.Add("ぶどう");
// インデックスでアクセス
Console.WriteLine($"最初の果物: {fruits[0]}");
// 要素数の取得
Console.WriteLine($"果物の数: {fruits.Count}");
// 要素の削除
fruits.Remove("バナナ");
// foreach文で繰り返し処理
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
}
}
出力結果:
最初の果物: りんご
果物の数: 4
りんご
オレンジ
ぶどう
機能比較表
機能 | IEnumerable | List |
---|---|---|
データの格納 | ❌ | ✅ |
foreach文 | ✅ | ✅ |
インデックスアクセス | ❌ | ✅ |
要素の追加 | ❌ | ✅ |
要素の削除 | ❌ | ✅ |
要素数の取得 | ❌* | ✅ |
LINQ | ✅ | ✅ |
メモリ効率 | ✅ | △ |
*Count()
拡張メソッドで可能ですが、全要素を数える必要があります
使い分けのポイント
IEnumerableを使う場面
IEnumerableが適している場面:
- データを読み取り専用で使用する
- メソッドの戻り値として「何かの集合」を返したい
- LINQクエリの結果を扱う
- メモリ使用量を抑えたい
Listを使う場面
Listが適している場面:
- 要素の追加・削除を行う
- インデックスで特定の要素にアクセスしたい
- 要素数を頻繁に取得する
- データを動的に変更する
パフォーマンスの違い
メモリ使用量の比較
// IEnumerableの例(遅延評価)
IEnumerable<int> numbers1 = Enumerable.Range(1, 1000000);
// この時点ではメモリにデータは作られていない
// Listの例(即座に評価)
List<int> numbers2 = Enumerable.Range(1, 1000000).ToList();
// この時点で100万個の整数がメモリに作られる
メモリ使用量の違い:
-
numbers1
: ほぼ0MB(データはまだ生成されていない) -
numbers2
: 約4MB(100万個のint型データが即座にメモリに格納)
アクセス速度の比較
List<int> list = new List<int> { 1, 2, 3, 4, 5 };
IEnumerable<int> enumerable = list;
// Listのインデックスアクセス: O(1) - 高速
int value1 = list[2];
// IEnumerableで特定要素にアクセス: O(n) - 遅い
int value2 = enumerable.ElementAt(2);
実行時間の違い:
-
list[2]
: 瞬時(直接アクセス) -
enumerable.ElementAt(2)
: 要素数に比例(先頭から順番にアクセス)
出力結果:
value1 = 3 (高速)
value2 = 3 (低速だが同じ結果)
実践的な例:データベースからのデータ取得
public class ProductService
{
// 良い例:戻り値にIEnumerableを使用
public IEnumerable<Product> GetActiveProducts()
{
// データベースから取得したデータを順次返す
// 必要な分だけメモリに読み込まれる
return dbContext.Products.Where(p => p.IsActive);
}
// 使用例
public void DisplayProducts()
{
IEnumerable<Product> products = GetActiveProducts();
// 必要に応じてListに変換
if (needsRandomAccess)
{
List<Product> productList = products.ToList();
Console.WriteLine($"最初の商品: {productList[0].Name}");
}
else
{
// 順次処理する場合はそのまま使用
foreach (var product in products)
{
Console.WriteLine(product.Name);
}
}
}
}
想定される出力結果:
// needsRandomAccess = true の場合
最初の商品: ノートパソコン
// needsRandomAccess = false の場合
ノートパソコン
マウス
キーボード
モニター
実際に動かせる完全なサンプル
より理解を深めるために、実際に動かせる完全なサンプルコードも紹介します。
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
Console.WriteLine("=== IEnumerable vs List 比較デモ ===\n");
// 1. 基本的な違いのデモ
Console.WriteLine("1. 基本的な使い方の違い");
DemonstrateBasicDifferences();
Console.WriteLine("\n2. パフォーマンスの違い");
DemonstratePerformanceDifferences();
Console.WriteLine("\n3. 遅延評価のデモ");
DemonstrateLazyEvaluation();
}
static void DemonstrateBasicDifferences()
{
// IEnumerable: 読み取り専用として使用
IEnumerable<string> colors = GetColors();
Console.WriteLine("IEnumerableから取得した色:");
foreach (var color in colors)
{
Console.WriteLine($" - {color}");
}
// List: 変更可能なコレクションとして使用
List<string> colorList = colors.ToList();
colorList.Add("紫");
colorList.Remove("赤");
Console.WriteLine("\nListに変換後(紫を追加、赤を削除):");
for (int i = 0; i < colorList.Count; i++)
{
Console.WriteLine($" [{i}] {colorList[i]}");
}
}
static void DemonstratePerformanceDifferences()
{
var numbers = Enumerable.Range(1, 1000000);
// IEnumerable: 遅延評価
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
IEnumerable<int> enumerable = numbers.Where(x => x % 2 == 0);
stopwatch.Stop();
Console.WriteLine($"IEnumerable作成時間: {stopwatch.ElapsedMilliseconds}ms");
// List: 即座に評価
stopwatch.Restart();
List<int> list = numbers.Where(x => x % 2 == 0).ToList();
stopwatch.Stop();
Console.WriteLine($"List作成時間: {stopwatch.ElapsedMilliseconds}ms");
Console.WriteLine($"List要素数: {list.Count}個");
}
static void DemonstrateLazyEvaluation()
{
Console.WriteLine("遅延評価のデモ:");
var lazyNumbers = GetNumbersWithLogging();
Console.WriteLine("GetNumbersWithLogging()呼び出し完了(まだ実行されていない)");
Console.WriteLine("\nforeachで実際に取得開始:");
foreach (var num in lazyNumbers.Take(3))
{
Console.WriteLine($"取得した値: {num}");
}
}
static IEnumerable<string> GetColors()
{
yield return "赤";
yield return "青";
yield return "緑";
yield return "黄";
}
static IEnumerable<int> GetNumbersWithLogging()
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($" 数値 {i} を生成中...");
yield return i;
}
}
}
出力結果:
=== IEnumerable vs List 比較デモ ===
1. 基本的な使い方の違い
IEnumerableから取得した色:
- 赤
- 青
- 緑
- 黄
Listに変換後(紫を追加、赤を削除):
[0] 青
[1] 緑
[2] 黄
[3] 紫
2. パフォーマンスの違い
IEnumerable作成時間: 0ms
List作成時間: 156ms
List要素数: 500000個
3. 遅延評価のデモ:
遅延評価のデモ:
GetNumbersWithLogging()呼び出し完了(まだ実行されていない)
foreachで実際に取得開始:
数値 1 を生成中...
取得した値: 1
数値 2 を生成中...
取得した値: 2
数値 3 を生成中...
取得した値: 3
このサンプルから分かること:
- IEnumerable: 作成時間がほぼ0ms(遅延評価のため)
- List: 作成時に全データを処理するため時間がかかる
- 遅延評価: 実際に値が必要になるまで処理が実行されない
まとめ
選択基準 | 推奨 |
---|---|
読み取り専用でデータを扱う | IEnumerable |
要素の追加・削除が必要 | List |
インデックスアクセスが必要 | List |
メモリ効率を重視 | IEnumerable |
メソッドの戻り値 | IEnumerable |
一時的なデータ操作 | List |
覚えておきたいポイント
- IEnumerableは「読み取り専用の集合」として使う
- Listは「変更可能なデータの容器」として使う
- 迷ったら「変更する必要があるか?」を考える
-
ToList()
でIEnumerableをListに変換できる - 逆に、ListはIEnumerableとしても使える
この理解があれば、適切な場面で適切なコレクションを選択できるようになります!