2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#】IEnumerableとListの違い

Posted at

C#におけるIEnumerableListの違い

はじめに

C#でプログラミングを始めると、コレクション(データの集まり)を扱う機会が多くあります。その中でもよく使われるのがIEnumerableListです。しかし、初学者の方には「いつどちらを使えばいいの?」「何が違うの?」という疑問があると思います。

この記事では、IEnumerableListの違いを図解とサンプルコードを使って、わかりやすく説明します。

基本的な概念の違い

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

覚えておきたいポイント

  1. IEnumerableは「読み取り専用の集合」として使う
  2. Listは「変更可能なデータの容器」として使う
  3. 迷ったら「変更する必要があるか?」を考える
  4. ToList()でIEnumerableをListに変換できる
  5. 逆に、ListはIEnumerableとしても使える

この理解があれば、適切な場面で適切なコレクションを選択できるようになります!

2
3
4

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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?