ジェネリクスの基本
同じロジックを複数の型に対して再利用したいとき、object 型で汎用化する方法は型安全性を失います。ジェネリクスは型をパラメータとして受け取ることで、コンパイル時の型チェックを維持したまま再利用できるしくみです。
学習目標
object を使った汎用化の問題点を説明できる
- 型パラメータ
<T> を使ったジェネリッククラスを定義できる
- 具体型を指定してインスタンス化できる
- 複数の型パラメータを持つクラスを書けるようになる
前提知識
1. object 型による汎用化の問題
int と string の両方を格納できるコンテナを作りたいとします。object を使えば 1 つのクラスで対応できますが、取り出すときにキャストが必要になり、誤った型のキャストはランタイムエラーになります。
1
2
3
4
5
6
7
| class Container
{
private object _value;
public void Set(object value) { _value = value; }
public object Get() { return _value; }
}
|
1
2
3
4
5
6
| Container c = new Container();
c.Set(42);
// キャストが必要で、間違えると実行時例外になる
int n = (int)c.Get();
string s = (string)c.Get(); // ❌ 実行時に InvalidCastException
|
コンパイラは型の誤りを検出できません。
2. ジェネリッククラス
書式:ジェネリッククラスの定義
1
2
3
4
| class クラス名<型パラメータ>
{
// 型パラメータをフィールドやメソッドの型として使う
}
|
| 要素 |
説明 |
<型パラメータ> |
任意の名前を付けられる。慣例として T・TKey・TValue などが使われる |
1
2
3
4
5
6
7
| class Container<T>
{
private T _value;
public void Set(T value) { _value = value; }
public T Get() { return _value; }
}
|
型パラメータ T はクラス定義の中で通常の型と同じように使えます。
3. インスタンス化
書式:ジェネリッククラスのインスタンス化
1
| クラス名<具体的な型> 変数名 = new クラス名<具体的な型>();
|
1
2
3
4
5
6
7
| Container<int> intContainer = new Container<int>();
intContainer.Set(42);
int n = intContainer.Get(); // キャスト不要。int として返る
Container<string> strContainer = new Container<string>();
strContainer.Set("hello");
string s = strContainer.Get(); // string として返る
|
1
2
| // 誤った型を渡すとコンパイルエラーになる
// intContainer.Set("hello"); ❌ コンパイルエラー
|
object 版と違い、誤った型の代入はコンパイル時に検出されます。
4. 複数の型パラメータ
型パラメータは複数持てます。2 つの値をペアで保持するクラスの例です。
書式:複数型パラメータ
1
| class クラス名<型パラメータ1, 型パラメータ2>
|
1
2
3
4
5
6
7
8
9
10
11
| class Pair<T1, T2>
{
public T1 First { get; }
public T2 Second { get; }
public Pair(T1 first, T2 second)
{
First = first;
Second = second;
}
}
|
1
2
3
| Pair<int, string> pair = new Pair<int, string>(1, "alpha");
Console.WriteLine(pair.First); // 1
Console.WriteLine(pair.Second); // alpha
|
まとめ
object による汎用化はキャストが必要で型安全でない
- ジェネリッククラスは
class クラス名<T> の形で定義する
- インスタンス化するとき
new クラス名<具体的な型>() と書く
- 型の誤りはコンパイル時に検出される
- 型パラメータは複数持てる(
<T1, T2>)
理解度チェック
object を使った汎用化の問題点は何ですか?
-
次のクラスを double 型で使うには、インスタンス化をどう書きますか?
1
2
3
4
| class Container<T>
{
public T Value { get; set; }
}
|
- (応用)
Pair<T1, T2> のインスタンスで First と Second を入れ替えて返すメソッドを追加してください。
解答を見る
-
取り出すときにキャストが必要で、型が合わなかった場合は実行時例外になります。コンパイラが型の誤りを検出できません。
-
Container<double> c = new Container<double>();
-
1
2
3
4
| public Pair<T2, T1> Swap()
{
return new Pair<T2, T1>(Second, First);
}
|
次のステップ
ジェネリックメソッド では、クラスではなくメソッド単体に型パラメータを付ける方法と、型推論のしくみを学びます。