ジェネリックメソッドとは?―「型だけ違う同じ処理」を1つにまとめよう【C#入門】
C#を学んでいると、こんな書き方を目にすることがあります。
Show<int>(10);
Show<string>("こんにちは");
そしてメソッド側を見ると、次のように書かれています。
static void Show<T>(T value)
{
Console.WriteLine(value);
}
初めて見ると、次のような疑問がわいてくるかもしれません。
<T>って何?- なぜ普通のメソッドではダメなの?
T valueとは何?<int>や<string>は何を指定しているの?
この記事では、いきなり難しい定義から入らず、「型だけ違う、ほとんど同じメソッドが増えてきたらどうする?」というところから、ジェネリックメソッドを考えていきます。
- 1. この記事で分かること
- 2. まず、普通のメソッドを確認しよう
- 3. string も表示したくなった
- 4. 型だけ違うメソッドが増えていく
- 5. ジェネリックメソッドにしてみよう
- 6. T は「型を入れる箱」のようなもの
- 7.
<T>とT valueは役割が違う - 8. 実際に動かしてみよう
- 9.
<int>は省略できることが多い - 10. 戻り値にも T を使える
- 11. 2つの値を入れ替える例
- 12.
List<int>で見たことがあるかもしれない - 13. Unity でもジェネリックメソッドは出てくる
- 14. なぜ
objectではダメなの? - 15. T という名前でなければダメ?
- 16. すべてをジェネリックにすればいいわけではない
- 17. まずはこの3つを覚えよう
- 18. まとめ
この記事で分かること
- ジェネリックメソッド
Show<T>(T value)の読み方 <T>とT valueの役割の違い<int>を省略できる理由(型推論)List<int>や Unity のGetComponent<T>()とのつながりobjectを使う方法との違い
| 項目 | 内容 |
|---|---|
| 対象 | C#を学び始めたばかりの方 |
| 前提 | メソッドの定義と呼び出し、引数・戻り値が分かる |
| 実行環境 | コンソールアプリ(.NET)で動作確認できます |
まず、普通のメソッドを確認しよう
たとえば、整数を表示するメソッドを作ってみます。
static void Show(int value)
{
Console.WriteLine(value);
}
呼び出すときは Show(10); です。引数 value には int 型の値が入ります。ここは特に難しくありません。
string も表示したくなった
今度は文字列も表示したくなったとします。
static void Show(string value)
{
Console.WriteLine(value);
}
これで Show("こんにちは"); と書けます。ここで、2つのメソッドを並べて比べてみましょう。
static void Show(int value)
{
Console.WriteLine(value);
}
static void Show(string value)
{
Console.WriteLine(value);
}
違うのは int と string の部分だけです。中身の処理 Console.WriteLine(value); はまったく同じです。
型だけ違うメソッドが増えていく
さらに double も表示したくなると、次のようになります。
static void Show(int value)
{
Console.WriteLine(value);
}
static void Show(string value)
{
Console.WriteLine(value);
}
static void Show(double value)
{
Console.WriteLine(value);
}
もちろん、これはC#として間違いではありません。引数の型が違う同じ名前のメソッドを作る方法は、オーバーロードと呼ばれます。
しかし今回は、どのメソッドも処理がまったく同じで、違うのは型だけです。そこで、次のように考えます。
「型の部分も、変数のように“あとから入れる”ことはできないだろうか?」
それを実現するのが、ジェネリックメソッドです。
ジェネリックメソッドにしてみよう
先ほどの3つのメソッドを、1つにまとめてみます。
static void Show<T>(T value)
{
Console.WriteLine(value);
}
見慣れない <T> と T value が出てきました。ここでの T は、あとから決める型につけた仮の名前だと考えてください。

T は「型を入れる箱」のようなもの
普通の変数は、int number = 10; のように、number という名前の場所に値を入れます。
ジェネリックでは少し考え方が違います。Show<T> の T には、値ではなく型が入ります。
| 呼び出し方 | T に入る型 | イメージ上のメソッド |
|---|---|---|
Show<int>(10); | int | static void Show(int value) |
Show<string>("こんにちは"); | string | static void Show(string value) |
Show<double>(3.14); | double | static void Show(double value) |
呼び出すときに < > の中へ型を書くと、その型が T の場所にはまる、というイメージです。
<T> と T value は役割が違う
ここは最初につまずきやすいところです。static void Show<T>(T value) には T が2回出てきますが、役割が違います。
Show<T>
↑
「このメソッドでは、型をあとから決めます」という宣言
T value
↑
決まった型を、引数 value の型として使います
| 書いてある場所 | 役割 |
|---|---|
Show<T> | 型をあとから決める、という宣言 |
T value | 宣言した T を、引数の型として使う |
実際に動かしてみよう
次のコードを試してみましょう。
using System;
class Program
{
static void Show<T>(T value)
{
Console.WriteLine(value);
}
static void Main()
{
Show<int>(100);
Show<string>("こんにちは");
Show<double>(3.14);
}
}
実行結果は次のとおりです。
100
こんにちは
3.14
1つのメソッドで、int・string・double を扱えました。
<int> は省略できることが多い
実は、先ほどのコードは次のように書いても動きます。
Show(100);
Show("こんにちは");
Show(3.14);
C#が、渡された値から T の型を判断してくれるからです。この仕組みを型推論といいます。
| 呼び出し | 渡した値 | C#の判断 |
|---|---|---|
Show(100); | 100 は int | T は int |
Show("こんにちは"); | "こんにちは" は string | T は string |
Show(3.14); | 3.14 は double | T は double |
そのため実際のコードでは、Show<int>(100); よりも Show(100); と書かれることが多くあります。

戻り値にも T を使える
T は引数だけでなく、戻り値の型にも使えます。
static T GetValue<T>(T value)
{
return value;
}
使う側は次のようになります。
int number = GetValue(10);
string message = GetValue("こんにちは");
10 を渡せば int として返り、文字列を渡せば string として返ります。
2つの値を入れ替える例
ジェネリックメソッドらしさが分かりやすい例として、2つの値を入れ替えるメソッドを作ってみます。
static void Swap<T>(ref T a, ref T b)
{
T temp = a; // a を一時的に退避
a = b;
b = temp;
}
int で使う
int x = 10;
int y = 20;
Swap(ref x, ref y);
Console.WriteLine(x);
Console.WriteLine(y);
20
10
string で使う
string a = "Apple";
string b = "Banana";
Swap(ref a, ref b);
Console.WriteLine(a);
Console.WriteLine(b);
Banana
Apple
ジェネリックを使わなければ、Swap(int...)・Swap(string...)・Swap(double...) のように、型ごとに似たメソッドを作ることになります。ジェネリックなら Swap<T> の1つで済みます。
List<int> で見たことがあるかもしれない
ジェネリックという言葉を知らなくても、すでに使ったことがある人は多いと思います。
List<int> numbers = new List<int>();
List<string> names = new List<string>();
<int> は「このListでは int を扱います」という意味です。今回学んでいるジェネリックメソッドも、基本的な考え方は同じです。
| 書き方 | あとから決めるもの |
|---|---|
List<T> | クラスで扱う型 |
Show<T>() | メソッドで扱う型 |
Unity でもジェネリックメソッドは出てくる
Unityを使っていると、こんなコードを見かけます。
Rigidbody rb = GetComponent<Rigidbody>();
Transform t = GetComponent<Transform>();
GetComponent<T>() もジェネリックメソッドです。「どの種類のComponentが欲しいですか?」という問いに対して、< > の中で型を指定しています。
| 呼び出し | T に入る型 | 意味 |
|---|---|---|
GetComponent<Rigidbody>() | Rigidbody | このGameObjectから Rigidbody を探して取得する |
GetComponent<Transform>() | Transform | このGameObjectから Transform を探して取得する |
つまり GetComponent<T>() という1つの仕組みで、いろいろな種類のComponentを取得できるようになっています。Unityで突然出てくるように見える < > も、C#のジェネリックを知ると意味が見えやすくなります。
GetComponent<Transform>() と transform の違いは、こちらの記事で詳しく解説しています。
Unityの transform.Rotate() と GetComponent<Transform>().Rotate() は何が違う?
なぜ object ではダメなの?
ここまで読んで、「object を使えば何でも受け取れるのでは?」と思った人もいるかもしれません。
static void Show(object value)
{
Console.WriteLine(value);
}
Show(10);
Show("こんにちは");
確かに、表示するだけならこれでも動きます。しかしジェネリックには、元の型を保ったまま処理できるという大きな利点があります。
static T GetValue<T>(T value)
{
return value;
}
int number = GetValue(10); // 戻り値が int だと分かっている
object で受け取って返すと、戻り値はただの object になり、int として使うにはキャスト(型変換)が必要になります。
static object GetValueObj(object value)
{
return value;
}
int number = (int)GetValueObj(10); // キャストが必要
ジェネリックは「何でも扱えるようにしながら、型の情報もできるだけ失わない」ための仕組み
T という名前でなければダメ?
いいえ。T でなければならないわけではありません。
static void Show<TValue>(TValue value)
{
Console.WriteLine(value);
}
ただし、単純なジェネリックでは T(Type の頭文字)がよく使われます。複数の型を扱う場合は、TKey・TValue・TItem のように意味の分かる名前をつけることもあります。
すべてをジェネリックにすればいいわけではない
ジェネリックは便利ですが、何でもジェネリックにすればよいわけではありません。
static int Add(int a, int b)
{
return a + b;
}
このように「int を扱うメソッドだ」とはっきり決まっているなら、無理にジェネリックにする必要はありません。ジェネリックが役に立つのは、処理は同じなのに、扱う型だけが違うという場面です。
まずはこの3つを覚えよう
最初からジェネリックのすべてを理解する必要はありません。まずは次の3つで十分です。
① <T> は「型をあとから決める」
static void Show<T>(T value)
T は、あとから決める型の仮の名前です。
② 同じ処理をいろいろな型で使える
Show(10);
Show("こんにちは");
Show(3.14);
③ Unity でも使われている
GetComponent<Rigidbody>();
ジェネリックはC#だけの難しい文法として覚えるのではなく、Unityで普段使っているコードを理解するための知識として見ると、意味がつかみやすくなります。
まとめ
| 普通のメソッド | ジェネリックメソッド | |
|---|---|---|
| 書き方 | static void Show(int value) | static void Show<T>(T value) |
| 扱う型 | あらかじめ決まっている | 呼び出すときに決まる |
| 型ごとのメソッド | 型の数だけ必要 | 1つで済む |
<T>は「型をあとから決める」という宣言、T valueはその型を引数に使う書き方Show(100);のように、型は渡した値から推論されることが多いList<int>やGetComponent<T>()も同じ考え方objectと違い、元の型の情報を保ったまま扱える
ジェネリックメソッドを初めて見たときは、<T> という記号に目が行きがちです。しかし、本当に大事なのは記号そのものではありません。
処理は同じ。違うのは型だけ。
なら、その型をあとから決められるようにしよう。
これが、ジェネリックメソッドを理解する最初の一歩です。













ディスカッション
コメント一覧
まだ、コメントがありません