genericsはC++のテンプレートと同じようなものです。テンプレートの場合、型の解決は実際のコード中でそのテンプレートが利用されるときに行われますが、genericsではgenericを使ったクラスがあった場合、そのクラスを使うコードがなくてもコンパイルできなければなりません。実行時に動的にそのgenericsを利用可能にするためです。そのため以下のような問題があります。
以下のようなC++のテンプレートを考えます。
class C { public: int func() { return 1; } }; template<typename T> class Caller { public: int DoFunc(T& t) { return t.func(); } }; int main() { C c; Caller<C> caller; int i = caller.DoFunc(c); return 0; }
これをC++/CLIのgenericsを使って書くと以下のようになり、コンパイルエラーとなります。
ref class C { public: int func() { return 1; } }; generic<typename T> ref class Caller { public: int DoFunc(T t) { return t->func(); // error } }; int main() { C^ c = gcnew C; Caller<C^>^ caller = gcnew Caller<C^>; int i = caller->DoFunc(c); return 0; }
型Tはコンパイル時に解決しないため、Object型としかみなせないためです。そのためgenericsでは以下のような方法を利用します。
interface class I { int func(); }; ref class C : I { public: virtual int func() { return 1; } }; generic<typename T> where T : I ref class Caller { public: int DoFunc(T t) { return t->func(); } }; int main() { C^ c = gcnew C; Caller<C^>^ caller = gcnew Caller<C^>; int i = caller->DoFunc(c); return 0; }
インターフェースIを利用して、型Tに縛りをかけます。
その他の注意点として、genericの型はハンドルか値型でなければなりません。