Genericsメソッド


メソッドスコープの型パラメータ

static メソッドのコレクションであるユティリティクラスなどでは、基本的にインスタンスを作ることができません。 このようなクラスの static メソッドが汎用的な処理を行いたい場合、インスタンス生成時に型を決定する型パラメータを使うことができません。 そこで、メソッド単位で型パラメータを指定することもできます。

メソッドに型パラメータを指定した場合、インスタンス生成式ではなくメソッド起動式で、メソッド宣言で指定されている型パラメータに型を指定します。 この場合、型パラメータはメソッドスコープであり、メソッドの中でのみ有効となります。 メソッドで型パラメータを指定するには次のように記述します。

修飾子 戻り値 メソッド名<型パラメータリスト>(仮引数リスト) { 本体 }

仮パラメータリストで宣言した仮パラメータは、メソッドの戻り値や仮引数リストで使うことも可能です。 次のメソッドは、仮パラメータを用いた有効な定義です。

class GTest {
	public static T[] CreateArray<T>(int length) {
		return new T[length];
	}
}

このメソッドは、指定したサイズの型パラメータ T 型の配列を作成して返すというものです。 型パラメータが指定されているメソッドは、従来のメソッド起動式では呼び出すことができません。 メソッド起動式でも、メソッド名の直後に型パラメータに従って適切な型を指定しなければならないのです。 上記の CreateArray() メソッドを呼び出すには次のように記述しなければなりません。

int [] iAry = GTest.CreateArray<int>(10);

このメソッド起動式では、CreateArray() メソッドの型パラメータに対して int 型を指定しているため、CreateArray() メソッドの定義における T 型は int に置き換えられて実行されると考えることができます。 そのため、この呼び出しによる CreateArray() メソッドは int 型の配列を作成して返すと考えられます。

class GTest {
	public static T[] CreateArray<T>(int length) {
		return new T[length];
	}
}

class Test {
	static void Main() {
		int[] iAry = GTest.CreateArray<int>(10);
		string[] strAry = GTest.CreateArray<string>(5);

		System.Console.WriteLine("iAry=" + iAry.Length);
		System.Console.WriteLine("strAray=" + strAry.Length);
	}
}
TiAry=10
strAray=5s

このプログラムは、指定した型の配列を作成して返す CreateArray() メソッドを作成しています。 このメソッドは型パラメータによって汎用化されているため、メソッドが呼び出されるまで作成する型は決定されません。 Main() メソッドでは int 型の配列と string 型の配列を作成していることがわかります。


Genericsメソッドの制約

メソッドに指定されている型パラメータに対して制約を与えることも可能です。 この場合は、メソッドの仮引数リストの直後、メソッド本体の直前に where 句を指定しなければなりません。

修飾子 戻り値 メソッド名<型パラメータリスト>(仮引数リスト) where句 { 本体 }

where 句の書き方はクラスや構造体に指定する where 句と同じです。

class GTest {
	public static T[] CreateArray<T> (int length) where T: class {
		return new T[length];
	}
}

class Test {
	static void Main() {
		//int[] iAry = GTest.CreateArray<int>(10);	//エラー
		string[] strAry = GTest.CreateArray<string>(5);

		System.Console.WriteLine("strAray=" + strAry.Length);
	}
}

このプログラムは、前のプログラムで作成した GTest クラスの CreateArray() メソッドに指定されている型パラメータ T に対して、参照型のみ許可するプライマリ制約を与えています。 そのため、CreateArray() メソッド呼び出し時に、型パラメータに値型を渡した場合はコンパイルエラーとなることが確認できます。


型パラメータを持つクラス型の引数

メソッドの引数に型パラメータを持つクラス型の引数を指定する場合に注意点があります。

メソッドの引数に、汎用的な型として型パラメータを持つクラス型のオブジェクトを受け入れたい場合、どのように記述するべきでしょうか。 例えば、次のような表現は問題ありません。

public void MethodName(Generics1<int> value) ...

このとき Generics1 クラスは型パラメータを T を持つと想定します。 このメソッドでは、Generics1 クラスの int 型の型パラメータのインスタンスしか受け入れないことを表明しています。

しかし、一般的には型パラメータを持つクラス型をやり取りする場合、やはり型を特定せずに抽象的に扱いたいと考えることでしょう。 そこで、次のようにメソッドに対して型パラメータを指定することで、メソッド呼び出し時にクラスに指定する型パラメータを決定することができるようになります。

public void MethodName<T>(Generics1<T> value) ...

ここまでは良いのですが、Generics1 クラスの型パラメータに何らかの制約が与えられている場合、このプログラムはコンパイルエラーとなります。 型パラメータを持つクラスに対して型パラメータを渡した場合、型パラメータに与えられている制約を満たす必要があります。

class GTest<T> where T: struct {}

class Test {
	static void Main() {
		GTest<int> obj = new GTest<int>();
		ShowGTest<int>(obj);
	}
	static void ShowGTest<T>(GTest<T> value) {
		System.Console.WriteLine(value);
	}
}

このプログラムをコンパイルしようとして、エラーが発生します。 原因は、GTest クラスの型パラメータ T に対して ShowGTest メソッドの型パラメータ T を指定してますが、ShowGTest メソッドの T と GTest クラスの型パラメータ T の制約が異なるためエラーとなるのです。 このままでは、ShowGTest メソッドの T に対して参照型が指定される危険性があります。

この問題を解決するには、ShowGTest メソッドの T に対して struct 制約を与える必要があります。

class GTest<T> where T: struct {}

class Test {
	static void Main() {
		GTest<int> obj = new GTest<int>();
		ShowGTest<int>(obj);
	}
	static void ShowGTest<T>(GTest<T> value) where T: struct{
		System.Console.WriteLine(value);
	}
}

これで、ShowGTest メソッドの型パラメータ T が、GTest クラスの型パラメータ T の制約を満たしていると考えられるため、無事にコンパイルすることができるようになります。

また、このようなメソッド起動式において、引数に与えられたオブジェクトの型から型パラメータの型をコンパイラが予測できる場合、メソッド起動式で型パラメータに指定する型を省略することができます。

class GTest<T> {}

class Test {
	static void Main() {
		ShowGTest(new GTest<int>());
		ShowGTest(new GTest<bool>());
		ShowGTest<string>(null);
	}
	static void ShowGTest<T>(GTest<T> value) {
		System.Console.WriteLine(typeof(T));
	}
}

このプログラムの Main() 関数では、ShowGTest() メソッドの呼び出しで型パラメータを指定していません。 ShowGTest() メソッドは T 型パラメータを宣言していますが、この場合は与えられた引数の型から T 型を決定することができるためです。 ShowGTest() メソッドの T 型パラメータはそのまま GTest クラス型の型パラメータに指定されているため、引数に指定された GTest<T> 型のオブジェクト value が持っている型パラメータの型から判断することができると考えられます。



前のページへ戻る次のページへ