【C#】ジェネリック(ジェネリクス)

ジェネリックとは

ジェネリックの意味

型パラメーターを使用することで、汎用的なクラスやメソッドなどを作ることができる機能。

Microsoftのドキュメント(英語版)ではgenericsとなっており、Javaなどではジェネリクスという訳語が一般的だが、Microsoftドキュメント(日本語版)ではジェネリックという訳語になっている。

英単語のgenericは、「汎用の」「一般的な」等の意味の形容詞。

ジェネリックを使うメリット

ジェネリックを使うと、クラス、構造体、インターフェイス、およびメソッドに、データ型のパラメーターを持たせることができる。

ジェネリックが存在しない場合、同じ処理をするメソッドでも、型ごとに別のメソッドを作らなければならない。

例えば、2つの値の比較をするメソッドの場合、オーバーロードを使ったとしても、Compare(int x,int y)、Compare(double x,double y)、Compare(long x,long y)、Compare(string x,string y)・・・と型の数だけのメソッドを作る必要がある。

ジェネリックを使うと、Compare<T>というメソッド一つで済ませることができる。

型パラメーターと型引数

型パラメーターは、Microsoftドキュメント(英語版)では type parameter となっている。

型引数は、Microsoftドキュメント(英語版)では type argument となっている。

つまり、パラメーター(仮引数)と引数(実引数)の違いと同じである。

型パラメーターは、メソッド定義に記述する、型引数を受けるための変数であり、

型引数はメソッド呼び出しで指定する実際の型である。

ジェネリックメソッド

ジェネリックメソッドは以下のように定義する。whereについては後述の型パラメーターの制約を参照。

ジェネリックがないと、以下のように処理内容は全く同じでも、型が違うだけで別のメソッドを作らなければならない。

ジェネリックメソッドを使うと、上記のようなメソッドを1つにまとめることができる。山かっこ<>を使って指定しているものが型パラメーターである。

呼び出し側は以下のようになる。<>に型引数を指定する。

ジェネリッククラス

ジェネリックなクラスを作成することもできる。whereについては後述の型パラメーターの制約を参照。

以下の例では単純なクラスを用いているが、実際にクラスでジェネリックを使う場合は、コレクションクラスなどの集合を表すクラスが多い。ジェネリッククラスは、主にコレクションクラスのために存在している。

以下はジェネリッククラスに静的フィールドを定義した例。型引数が違うと、違う型として認識されていることがわかる。

変数の宣言には型推論(var)を使うことができる。冗長性を避けるため通常はvarで宣言する。

以下のように、型パラメーターを複数定義することもできる。

型パラメーターの制約

whereで制約条件を指定すると、引数にとれる型を限定することができる。

どんな型でも引数にできてしまうと、引数の型が持っていない機能をメソッドやクラス内で使ったときにエラーになってしまう。

制約にはいろいろな種類があるが、以下の例はインターフェイス制約を使った例である。

上記のコードでは、メソッド内でCompareToを使っている。CompareToを使うには、型がIComparableを実装している必要がある。
(※インターフェイスは規定されたメソッドの実装を強制する働きがある。IComparableを実装した型は、CompareToメソッドを実装しなければならない。IComparableを実装している型は必ずCompareToメソッドを持っている。)

つまり、上記のメソッドの場合、IComparableを実装している型しか引数にとることはできない。

したがって、CompareToを使うには、制約条件としてIComparableを指定する必要がある。この制約条件がないと、コンパイルエラーとなってしまう。

なお、ジェネリックでは型引数に必ずしも演算子が定義されているわけではないため、演算子は使用できなくなる。ただし、C#11のGeneric Mathの機能を使うことで、演算子を使用できるようになる。

制約には以下のような種類がある。
詳しくは「型パラメーターの制約 – C# プログラミング ガイド | Microsoft Docs」を参照

種類説明
where T : structTは値型でなければならない。
Tにはnull許容値型は使用できない。
他の制約がある場合、先頭に置く必要がある。
where T : classTは参照型でなければならない。
他の制約がある場合、先頭に置く必要がある。
where T : new()Tはパラメーターなしのパブリック コンストラクターを持たなければならない。
他の制約がある場合、最後に置く必要がある。
where T :クラス名Tは指定されたクラスであるか、そのクラスを継承していなければならない。
where T :
インターフェイス名
Tは指定されたインターフェイスであるか、そのインターフェイスを実装していなければならない。
複数のインターフェイス制約を指定することができる。
where T : unmanagedTはアンマネージ型でなければならない。
where T : EnumTは列挙型でなければならない。
where T : DelegateTはデリゲート型でなければならない。
where T : notnullTは非 null な型でなければならない。
制約の種類

型引数を複数指定する場合や、制約条件を複数指定する場合は以下のようにする。

すべての型の基底クラスであるobject型で定義されているメソッドは、制約なしで呼び出すことができる。

デフォルト値(ゼロ系の値)を指定する

型のデフォルト値は、数値型であれば0であり、参照型であればnull、bool型ではfalseとなる。

型パラメーターが何の型であるかはコンパイル時にはわからないため、コードにリテラルのデフォルト値を用いることはできない。

ジェネリックでデフォルト値を表すには、default(T)を用いる。

コメント

タイトルとURLをコピーしました