技術解説 .NET 2.0 ジェネリック(Generics) 第1回 はじめに
この文書はVisual Studio 2005 をベースに記述されています。
第1回 はじめに
おがわさんよりジェネリックについて説明してほしいと提案があったので連載開始します。
そのままジェネリックについて語っても面白くないので、.NET1.xでのやり方が.NET2.0でこう変わるといったかんじで進めていこう。
ジェネリックとは?
ジェネリックとはC++のSTL(スタンダード テンプレート ライブラリ)に源流を持つ由緒正しい考えである。
たとえばSTLの説明ではこのような売り文句が大体載っていたはずだ。
ソートアルゴリズムなどは同じアルゴリズムなのにCなどの型を意識する際にうまく利用できない。
そうするとソートアルゴリズム自体を移植しなくてはならず非効率だ。
そこでジェネリックだ。
作成時には型を決めずに、ソースコード上でint用のソート、float用のソートと宣言すれば利用できる仕組みがそれだ。
ま、こんなかんじだっただろうか。
型に依存しないプログラミングを目指すのがジェネリックだ。
さてそんな事をいってもよくわからないので、実際のコードを見てみよう。
たとえば1.xで自動拡張してくれるArrayListを使ってStringを格納する場合には以下のようなプログラムをしていたのではないだろうか?
1.x C# ArrayList al = new ArrayList(); al.Add("A"); al.Add("B"); al.Add("C"); if ( "A" == (string)al[0] ) { MessageBox.Show("A発見"); }
1.x VB Dim al As ArrayList = New ArrayList al.Add("A") al.Add("B") al.Add("C") If ("A" = CType(al(0), String)) Then MessageBox.Show("A発見") End If
1.x MC++ ArrayList __gc *al = new ArrayList(); al->Add(S"A"); al->Add(S"B"); al->Add(S"C"); if ( S"A" == static_cast<String*>(al->Item[0]) ) { System::Windows::Forms::MessageBox::Show(S"A発見"); }
ここでの問題点は(string)というキャスト(C#) / CTypeという型の変換(VB)を伴うことだ。
なぜならArrayListはすべてのものをobjectで管理するからだ。
objectで管理されているからには利用するには元の型に戻してあげなくてはいけない、そうすると型の変換で失敗したりする恐れがある。
もちろんそのArrayListに何を入れるのかは厳密に管理すべき性格のものだが、やはりダウンキャストのデメリットは残る。
そこでジェネリックの登場だ
2.0 C# List<string> ls = new List<string>(); ls.Add("A"); ls.Add("B"); ls.Add("C"); if ( "A" == ls[0] ) { MessageBox.Show("A発見"); }
2.0 VB Dim ls As List(Of String) = New List(Of String) ls.Add("A") ls.Add("B") ls.Add("C") If ("A" = ls(0)) Then MessageBox.Show("A発見") End If
2.0 C++/CLI List<String^>^ ls = gcnew List<String^>(); ls->Add("A"); ls->Add("B"); ls->Add("C"); if ( "A" == ls->default[0] ) { MessageBox::Show(gcnew String("A発見")); }
どうだろう、このList<String> / List(Of String)はstring専用になったArrayListだと思えばいい。
内部の管理はもちろんstringで行われているし、取り出しもstringだ、だからキャストが必要ない。
ちなみにこのようなジェネリックを利用したクラスはMSDNライブラリには List<T> と書かれる。
TとはタイプのTだが、Tである必要はない。
ただしインターフェイスのIと同じ用にプレフィックスとしてTを使うことになるようである。