ここで簡単に、ジェネリック・クラスの記述方法についても見ておきましょう。
以下は、Listジェネリック・クラスの仕様をまねたMyListジェネリック・クラスの実装例です。取りあえずAddメソッドとインデクサだけを実装しています。
public class MyList<T> {
T[] _list = new T[1000]; // 要素は最大1000個まで
int _size = 0;
// Addメソッド
public void Add(T item) {
_list[_size++] = item;
}
// インデクサ
public T this[int index] {
get {
return _list[index];
}
set {
_list[index] = value;
}
}
}
Public Class MyList(Of T)
Dim _list(1000) As T ' 要素は最大1000個まで
Dim _size As Integer = 0
' Addメソッド
Public Sub Add(ByVal item As T)
_list(_size) = item
_size += 1
End Sub
' デフォルト・プロパティ(インデクサとして機能)
Default Public Property Item(ByVal index As Integer) As T
Get
Return _list(index)
End Get
Set(ByVal value As T)
_list(index) = value
End Set
End Property
End Class
このように、ジェネリック・クラスの記述では、型パラメータを「T」とした場合、Tというクラスがあるかのようにフィールドやメソッドを記述していきます。
これまでに「T」の部分が具体的なクラス名になっているようなクラスを記述されたことがあるかもしれません。例えば「T」の部分がすべて「Employee」になっているようなEmployeeクラス専用のコレクション・クラスなどです。
ジェネリックの導入により、その「T」部分が自動的に置き換えられる、汎用的なコレクション・クラスが記述可能となったわけです。
なお、型パラメータである「T」は単なるパラメータの名前であり、メソッドのパラメータと同じで、好きな名前を付けることができます。ただし名前付けのガイドラインは用意されています*。型パラメータが1つだけの場合は「T」がよく用いられます。
*「型パラメータの名前付けのガイドライン」を参照してください。
念のために、上記のMyList<T>クラスを、文字列型とDateTime構造体で使用したサンプル・プログラムを次に示しておきます。
// MyList<T>クラスを利用
using System;
public class GenericSample {
static void Main() {
// 文字列用のリスト
MyList<string> strings = new MyList<string>();
strings.Add("こんにちわ");
strings.Add("さようなら");
string hello = strings[0];
Console.WriteLine(hello);
// 出力:こんにちわ
// DateTime用のリスト
MyList<DateTime> datetimes = new MyList<DateTime>();
datetimes.Add(DateTime.Today);
datetimes.Add(DateTime.MaxValue);
DateTime today = datetimes[0];
Console.WriteLine(today);
// 出力例:2006/02/09 0:00:00
}
}
' MyList(Of T)クラスを利用
Module Module1
Sub Main()
' 文字列用のリスト
Dim strings As New MyList(Of String)
strings.Add("こんにちわ")
strings.Add("さようなら")
Dim greeting As String = strings(0)
Console.WriteLine(greeting)
' 出力:こんにちわ
' DateTime用のリスト
Dim datetimes As New MyList(Of DateTime)
datetimes.Add(DateTime.Today)
datetimes.Add(DateTime.MaxValue)
Dim today As DateTime = datetimes(0)
Console.WriteLine(today)
' 出力例:2006/02/09 0:00:00
End Sub
End Module
【コラム】 型パラメータの制約
ジェネリック・クラスを記述するとき、型パラメータに指定される型を制限する必要が出てくる場合があります。
例えば、型パラメータの型(以下、Tと省略)のオブジェクトのメソッドを呼び出すようなコードを記述する場合、Tには必ずそのメソッドが実装されていなければ困ります。あるいは、Tのインスタンスを生成する(new T()する)ようなコードを記述する場合には、Tにパブリックな(パラメータを持たない)コンストラクタがなければ困ります。
ジェネリック・クラスでこういった制約(constraints)を記述するには、C#ではwhereキーワード、VBでは「Of T」の後にAsキーワードを使います。
以下に、記述可能な制約のパターンの一覧を実際のコードで示します(このコードはコンパイル可能です)。
class MyGenericClass1<T> where T : struct {
// 制約:Tは構造体
}
class MyGenericClass2<T> where T : class {
// 制約:Tはクラス
}
class MyGenericClass3<T> where T : new() {
// 制約:Tはインスタンス化可能
}
class MyGenericClass4<T> where T : MyOtherClass {
// 制約:TはMyOtherClassクラスを継承
}
class MyGenericClass5<T> where T : IMyInterface {
// 制約:TはIMyInterfaceインターフェイスを実装
}
class MyGenericClass6<T, U> where T : U {
// 制約:Tは別の型パラメータUを継承
}
//////////////////////////////////////////////////
class MyOtherClass {
// とあるクラス
}
interface IMyInterface {
// とあるインターフェイス
}
Class MyGenericClass1(Of T As Structure)
' 制約:Tは構造体
End Class
Class MyGenericClass2(Of T As Class)
' 制約:Tはクラス
End Class
Class MyGenericClass3(Of T As New)
' 制約:Tはインスタンス化可能
End Class
Class MyGenericClass4(Of T As MyOtherClass)
' 制約:TはMyOtherClassクラスを継承
End Class
Class MyGenericClass5(Of T As IMyInterface)
' 制約:TはIMyInterfaceインターフェイスを実装
End Class
Class MyGenericClass6(Of T As U, U)
' 制約:Tは別の型パラメータUを継承
End Class
''''''''''''''''''''''''''''''''''''''''''''''''''
Class MyOtherClass
' とあるクラス
End Class
Interface IMyInterface
' とあるインターフェイス
End Interface
なおここでは説明は割愛しますが、1つの型パラメータに対して複数の制約を指定可能です。
Copyright© Digital Advantage Corp. All Rights Reserved.