【C#】ジェネリック(Generics)の基本|型安全なコレクションの作り方

C# の「ジェネリック(Generics)」は、クラスやメソッドを特定の型に依存しない形で設計できる仕組みです。これにより、再利用性の高いコードを記述でき、かつ型安全性を確保できます。特に List<T> などのコレクションでは必須の概念です。本記事ではジェネリックの基本と、型安全なコレクションの作り方を解説します。

ジェネリックを使わない場合の問題

ジェネリックを使わない古いコレクション(例:ArrayList)では、あらゆる型を格納できますが、取り出すときにキャストが必要になります。そのため実行時エラーの原因になりやすいです。

using System;
using System.Collections;

class Program
{
    static void Main()
    {
        ArrayList list = new ArrayList();
        list.Add(1);
        list.Add("text"); // 異なる型も追加可能

        int num = (int)list[0];  // OK
        int error = (int)list[1]; // 実行時に例外発生!
    }
}

ジェネリックを使った型安全なコレクション

List<T> などのジェネリックコレクションを使うと、格納する型をコンパイル時に指定でき、型安全性が保証されます。

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int>();
        numbers.Add(10);
        numbers.Add(20);
        // numbers.Add("text"); // コンパイルエラーになる

        foreach (int n in numbers)
        {
            Console.WriteLine(n);
        }
    }
}

このように、型が明確に制限されるため、不要なキャストや実行時エラーを防げます。

ジェネリッククラスの定義

自作クラスでもジェネリックを利用できます。以下はジェネリッククラスの基本例です。

class Box<T>
{
    private T content;

    public void Put(T item)
    {
        content = item;
    }

    public T Get()
    {
        return content;
    }
}

class Program
{
    static void Main()
    {
        Box<string> box1 = new Box<string>();
        box1.Put("Hello");
        Console.WriteLine(box1.Get()); // Hello

        Box<int> box2 = new Box<int>();
        box2.Put(123);
        Console.WriteLine(box2.Get()); // 123
    }
}

Box<T> のように <T> を使えば、型に依存しない汎用的なクラスを作成できます。

ジェネリックメソッド

クラス全体ではなく、メソッド単位でジェネリックを使うことも可能です。

class Program
{
    static void PrintTwice<T>(T value)
    {
        Console.WriteLine(value);
        Console.WriteLine(value);
    }

    static void Main()
    {
        PrintTwice("Hello");
        PrintTwice(123);
    }
}

ジェネリックの制約(where句)

ジェネリックには制約を付けることができ、特定の条件を持つ型だけを受け入れるようにできます。

class Repository<T> where T : class
{
    private List<T> items = new List<T>();

    public void Add(T item) => items.Add(item);
    public IEnumerable<T> GetAll() => items;
}

where T : class により、参照型のみを扱うリポジトリクラスとなります。

まとめ

ジェネリックを使うと、型安全性と再利用性を両立できます。

  • ジェネリックコレクション(例:List<T>)で型安全なデータ管理が可能
  • 自作クラスやメソッドにもジェネリックを導入できる
  • where 制約で型の条件を絞り込める

特にコレクション操作ではジェネリックが必須です。型安全性を意識しつつ、柔軟に再利用できるコード設計を心がけましょう。