C#で知られている「ジェネリック」は、型を決めずに処理などを共通化することができる、とても便利な機能になります。コーディングを行っていると型を超えて処理を共通化したい場合が出てきますが、そんなときに使えます。
この記事ではジェネリックのメソッドを作成する方法を解説します。ジェネリックメソッドの作成方法は、通常のメソッドを踏襲していますが、書き方が少し違うので混乱しないように注意しましょう。
ジェネリックメソッドのサンプル
ジェネリックは「型が異なるが処理が同じもの」を作るときにとても便利な機能になります。「異なる型」をまとめる方法はどうすればよいのでしょうか。一般的なサンプルとして紹介されるのが、二つの引数に対して大きいほうの値を返すメソッドになります。例えば int 型と double 型を使用して通常のメソッドを記述すると以下のようになります。
//App01
int a1 = 5, b1 = 6;
var ret1 = IntMax(a1, b1);
Console.WriteLine(ret1.ToString());
double a2 = 10, b2 = 13;
var ret2 = DoubleMax(a2, b2);
Console.WriteLine(ret2.ToString());
Console.ReadLine();
static int IntMax(int a, int b)
{
return a >= b ? a : b;
}
static double DoubleMax(double a, double b)
{
return a >= b ? a : b;
}
IntMax メソッドも DoubleMax メソッドも同じ処理をしています。通常では型が異なるため別メソッドで切り出す必要があります。こうした処理は一般化できたらコード行数を少なくすることができます。こういった場合にジェネリックを使用します。
//App02
int ret1 = Max<int>(10, 100);
Console.WriteLine(ret1.ToString());
double a = 0.1, b = 0.15;
double ret2 = Max(a, b);
Console.WriteLine(ret2.ToString());
float a1 = 10, b1 = 15;
var ret3 = Max<float>(a1, b1);
Console.WriteLine(ret3.ToString());
Console.ReadLine();
//ジェネリックメソッド
static T Max(T a, T b) where T : IComparable
{
return a.CompareTo(b) >= 0 ? a : b;
}
上記のサンプルではジェネリックメソッドを以下のように定義しています。
static T Max(T a, T b) where T : IComparable
{
return a.CompareTo(b) >= 0 ? a : b;
}
上記のメソッド1個で int, double, float の処理をカバーできていることが分かります。ジェネリックメソッドを使用することで、「処理は同じだけど型が異なる場合」も共通化できるのです。
ジェネリックメソッドの定義
ジェネリックメソッドの定義は以下のようになります。
アクセス修飾子 戻り値の型 メソッド名<型引数>()
{
//処理
}
アクセス修飾子は public や private などですね。サンプルでは static が使われていましたが、これはコンソールアプリケーションのメインクラスに直接記述していたからです。戻り値の型はメソッドの戻り値になります。メソッド名のあとに 「<>」を使用して、その間に型引数を記載します。
サンプルでは T が型を決定する役目を務めていました。サンプルでは T の型引数に対して、戻り値が同じ型だったため T を使用しています。もちろん、引数も呼び出し元で変わるので T を使用していました。ジェネリックでは、一般的に T が用いられることが多いようです。
型引数に制約を加える方法
ジェネリックメソッドを使用する場合、型引数を共通化することができるので便利ですが、どんな型でもOKとしてしまうのは不安です。それを防ぐため、型引数に制約を加えることができます。
サンプルではメソッド名の後に「where T : IComparable」という記載がありますが、この部分が制約を加えている部分になります。「T の型引数の条件として IComparable が実装されている型であること」を条件としています。
where 句を使用することでジェネリック型の型引数として使用される型が、指定されたクラスを基底クラスとして持っているか、または基底クラスであることを明示的に示すことができます。マイクロソフトのサンプルでは以下のようなものも挙げられていました。
public class UsingEnum where T : System.Enum { }
public class UsingDelegate where T : System.Delegate { }
public class Multicaster where T : System.MulticastDelegate { }
制約として加えられるものには様々なものがあり、クラスやインターフェースだけではありません。また複数の制約を加える場合は「,」を使うことで追加することが可能です。「Object、Array、ValueType」といった方は制約として許可されないので注意しましょう。