C#入門㉚ ジェネリクス ── 型を、後から決める
前回は「LINQ(データをラクに絞る・並べる)」をやりました。
今回は、少し抽象的ですが、C#のいろんな場面で出てくるジェネリクスです。実は、もうずっと使ってきたものの正体でもあります。
ジェネリクスとは、「型」を後から指定できるようにする仕組みです。
実は、ずっと使っていた
第12回の List を思い出してください。
List<int> numbers = new List<int>(); // 整数のリスト
List<string> names = new List<string>(); // 文字列のリスト
List<int>、List<string>。この < > の中に型を書く書き方、これがジェネリクスです。知らないうちに、ずっと使っていました。
List という同じ仕組みが、<int> なら整数用、<string> なら文字列用になる。ひとつの仕組みを、いろんな型で使い回せる。これがジェネリクスの正体です。
なぜ、こんな仕組みが必要なのか
もし、ジェネリクスが無かったら、どうなるか考えてみましょう。
「整数のリスト」「文字列のリスト」「Personのリスト」…。型ごとに、別々のリストを作らなければいけません。
IntList numbers; // 整数専用リスト
StringList names; // 文字列専用リスト
PersonList people; // Person専用リスト
// 型の数だけ、リストを作る…?
中身(リストの仕組み)はまったく同じなのに、型が違うだけで、何種類も作るのは無駄です。
そこで、「中身の型は、使うときに決めてね」としたのが List。
List<int> // 使うときに int を指定
List<string> // 使うときに string を指定
List<Person> // 使うときに Person を指定
リストの仕組みは1つ。型だけ、後から差し込む。お弁当箱でいえば、「箱の作りは同じで、何を詰めるかは後で決める」ようなものです。
自分でも作れる
ジェネリクスは、使うだけでなく、自分で作ることもできます。たとえば「何でも入る箱」を作ってみましょう。
class Box<T>
{
public T Content { get; set; }
}
<T> の T が、「後から決める型」を表します(Type の T)。
使うときに、T の中身を指定します。
Box<int> intBox = new Box<int>();
intBox.Content = 100; // int を入れる箱
Box<string> strBox = new Box<string>();
strBox.Content = "こんにちは"; // string を入れる箱
同じ Box という仕組みが、<int> なら整数の箱、<string> なら文字列の箱になりました。T の部分が、指定した型に置きかわるイメージです。
なぜ、型を分ける必要があるのか
「何でも入る箱なら、object 型を使えばいいのでは?」と思うかもしれません(C#には「何でも入る」型もあります)。
でも、それだと型のチェックが効かなくなります。第3回でやった「箱の種類が合わないとエラー」という安全装置が、働かなくなるんです。
Box<int> intBox = new Box<int>();
intBox.Content = "文字"; // int の箱に文字!→ エラーで気づける
ジェネリクスなら、「int の箱」と決めた以上、文字を入れようとした瞬間にエラーで教えてくれます。型の安全性を保ったまま、使い回せる。これがジェネリクスの一番の価値です。
どこで出会うか
ジェネリクスは、C#のあちこちで顔を出します。これまで出てきたものだけでも、
List<T>(第12回)… リストDictionary<TKey, TValue>(第14回)… 辞書int?… 実はNullable<int>というジェネリクス(第28回)
< > を見かけたら、「ああ、型を後から決めるやつだな」と思えば大丈夫。自分でゼロから作る機会は、最初は少ないかもしれません。でも「List はジェネリクスだったのか」と分かるだけで、C#の見え方が変わります。
つまずきポイント
初心者が戸惑うのは、<T> の T という記号です。
T は、ただの「仮の名前」です。「ここに、後で本物の型が入りますよ」という、場所取りのようなもの。T でなくても TItem でも構いませんが、慣習として T がよく使われます。
「T = まだ決まっていない型の、仮の名前」。そう思えば、難しくありません。Box<int> と書いた瞬間、T がぜんぶ int に置きかわる、とイメージしてください。
まとめ
ジェネリクスは、型を後から指定できる仕組み。
List<int> numbers; // 使うとき:型を指定
List<string> names;
class Box<T> // 作るとき:T で場所取り
{
public T Content { get; set; }
}
< >の中に型を書くのが、ジェネリクス- ひとつの仕組みを、いろんな型で使い回せる
- 型のチェック(安全性)は、保たれたまま
Tは「後で決める型」の仮の名前
次回は、ぐっと実務的に。ファイルの読み書き をやります。
(補足:<T> を使って、複数の型を共通の仕組みで扱える点は、第26回のインターフェースとも相性がよく、組み合わせるとさらに強力になります。今は「型を使い回す仕組み」とだけ押さえておけば十分です。)

