C# ジェネリクスの基本
ジェネリクス(Generics)は、型をパラメータ化する機能です。同じロジックを様々な型に対して再利用でき、型安全性を保ちながらコードの重複を減らせます。
ジェネリクスが解決する問題
ジェネリクスがない場合、異なる型に対して同じ処理を行うには、型ごとにコードを書くか、object 型を使う必要がありました。
// object を使う方法(型安全でない)
ArrayList list = new ArrayList();
list.Add(1);
list.Add("文字列"); // 異なる型も追加できてしまう
int value = (int)list[0]; // キャストが必要object を使うと型チェックがコンパイル時に行われず、実行時エラーの原因になります。
ジェネリックコレクション
ジェネリクスを使った List<T> では、格納できる型が制限されます。
List<int> numbers = new List<int>();
numbers.Add(1);
numbers.Add(2);
// numbers.Add("文字列"); // コンパイルエラー!
int first = numbers[0]; // キャスト不要<int> という型パラメータにより、int 型だけを格納するリストになります。
型安全性の確保
object を使う方法
どんな型でも格納可能。取り出し時にキャストが必要。実行時エラーの危険。
ジェネリクスを使う方法
指定した型のみ格納可能。キャスト不要。コンパイル時に型チェック。
様々なジェネリックコレクション
.NET には多くのジェネリックコレクションが用意されています。
List<T>
可変長の配列。最もよく使われるコレクション。
Dictionary<TKey, TValue>
キーと値のペアを格納。高速な検索が可能。
HashSet<T>
重複のない要素の集合。存在確認が高速。
Queue<T> / Stack<T>
先入れ先出し(FIFO)/ 後入れ先出し(LIFO)のコレクション。
// Dictionary の例
var scores = new Dictionary<string, int>();
scores["Alice"] = 95;
scores["Bob"] = 87;
Console.WriteLine(scores["Alice"]); // 95
// HashSet の例
var uniqueNumbers = new HashSet<int> { 1, 2, 3 };
uniqueNumbers.Add(2); // 重複は無視される
Console.WriteLine(uniqueNumbers.Count); // 3ボックス化の回避
ジェネリクスは値型(int, struct など)を扱う際のボックス化も回避します。List<int> は内部的に int 型の配列を保持するため、object への変換(ボックス化)が発生しません。
// ArrayList:int が object にボックス化される
ArrayList arrayList = new ArrayList();
arrayList.Add(42); // ボックス化が発生
// List<int>:ボックス化なし
List<int> genericList = new List<int>();
genericList.Add(42); // そのまま int として格納ジェネリクスは現代の C# プログラミングに欠かせない機能です。型安全性とパフォーマンスを両立させながら、再利用可能なコードを書くための基盤となっています。