この章のテーマ §
ジェネリックとジェネリック型制約を行うwhereキーワードについて学びます。ジェネリックは、任意の型を受け入れる型やメソッドを作り出す技術です。
前提知識 §
Console.WriteLineメソッド, クラス基礎, 変数の基礎, foreach文, メソッドの基礎, IEnumerable<T>インターフェース, int型, string型, コレクションの初期化構文
解説 §
任意の型を受け入れてリストとして管理するListクラスのようなクラスを作成するために使用される技術がジェネリックです。
ジェネリックを使うにはクラスやメソッドに不等号で囲んだ名前(たとえば<T>)を書きます。これが型引数(型パラメータ)です。型引数は型のように使いますが、実際に使用されるまで確定しません。たとえば、ListクラスならList<int>なら整数のリスト、List<string>なら文字列のリストとなります。
しかし、普通に使うと型引数を経由して呼び出せるのは【全ての型に確実に存在する機能のみ】であり、具体的にはobjectが持つToStringメソッドなどだけになります。これでは不便なので、型制約を付けることができます。型制約は、whereキーワードを使って"型引数:制約される型"のように書きます。そうすると、その型を利用でない型は型引数に指定できなくなりますが、その型の機能は全て呼べるようになります。サンプルソースの場合、where T : IEnumerable<int>として型引数TにIEnumerable<int>型の制約を課しています。このインターフェースは、配列のint[]やリストのList<int>が実装していますから、どちらでも利用できる指定となります。
制約には他にnew制約などもあります。new制約はnew()と制約条件に書き、"new演算子で作成可能な型"という制約を付けます。
罠の数々 §
参考リンク §
ジェネリック クラスとメソッド
型パラメーターの制約 (C# プログラミング ガイド)
new 制約 (C# リファレンス)
サンプルソース: kw_where §
Console.WriteLine("class MyClass<T>");
var x1 = new MyClass<int>();
x1.Sub(123);
var x2 = new MyClass<string>();
x2.Sub("Hello!");
Console.WriteLine("class MyClass2<T> where T : IEnumerable<int>");
var x3 = new MyClass2<int[]>();
x3.Sub(new int[] { 1, 2, 3 });
var x4 = new MyClass2<List<int>>();
x4.Sub(new List<int>() { 1, 2, 3 });
Console.WriteLine("class MyClass3<T> where T : new()");
var x5 = new MyClass3<MyDummy>();
x5.Sub();
// new 可能なダミーのクラス
class MyDummy
{
}
class MyClass<T>
{
public void Sub(T parameter)
{
Console.WriteLine(parameter?.ToString());
}
}
class MyClass2<T> where T : IEnumerable<int>
{
public void Sub(T parameter)
{
foreach (var item in parameter) Console.WriteLine($"member={item}");
}
}
class MyClass3<T> where T : new()
{
public void Sub()
{
var x = new T();
Console.WriteLine(x.ToString());
}
}
実行結果 §
class MyClass<T>
123
Hello!
class MyClass2<T> where T : IEnumerable<int>
member=1
member=2
member=3
member=1
member=2
member=3
class MyClass3<T> where T : new()
MyDummy
リポジトリ §
https://github.com/autumn009/CSharpPrimer2
練習問題 §
以下のプログラムの実行結果を予測してみよう。
sub<string>();
void sub<T>() where T : string
{
Console.WriteLine(typeof(T).Name);
}
- T
- string
- String
- コンパイルエラー
- 実行時例外
[[解答]]