型だけ違って処理の内容が同じようなものを作るときにオーバーロードさせて書くと、同じコードを書くことになるし、メンテコストも上がる。
なので、色々な型に対応した関数を作ることができる、ジェネリックスを使う。
参照サイト : ジェネリックス
ジェネリックメソッド
例えば、比較のための関数を作る場合、
- GenericTest.cs
using System; class GenericTest { public static Type Max<Type>(Type a, Type b) where Type : IComparable { return a.CompareTo(b) > 0 ? a : b; } } public class TestHoge { public static void Main() { int n1 = GenericTest.Max<int>(5, 10); Console.Write(n1 + "\n"); double n2 = GenericTest.Max(5.0, 10.0); Console.Write(n2 + "\n" ); string n3 = GenericTest.Max("aaa", "bbb"); Console.Write(n3 + "\n"); } }
- コンパイル & 実行
% mcs GenericTest.cs % mono GenericTest.exe 10 10 bbb
となる。
別にTypeじゃなくて、任意の型を指定できる。
using System; class GenericTest { public static A Max<A>(A a, A b) where A : IComparable { return a.CompareTo(b) > 0 ? a : b; } } public class TestHoge { public static void Main() { int n1 = GenericTest.Max<int>(5, 10); Console.Write(n1 + "\n"); double n2 = GenericTest.Max(5.0, 10.0); Console.Write(n2 + "\n" ); string n3 = GenericTest.Max("aaa", "bbb"); Console.Write(n3 + "\n"); } }
これでも、コンパイル通るし、実行結果は最初のコードと変わらず。
ジェネリッククラス
コレクションのクラス(配列とかリスト)作る場合にもジェネリックスが使えます。 ちょっとひねくれて、静的で試してみた。
using System; class Stack<T> { static T[] buf = new T[10]; static int top = 0; public static void Push(T val) {buf[top++] = val;} public static T Pop() { return buf[--top]; } public static int Size{ get{return top; } } } class Hoge { public static void Main() { for(int i=1; i<=10; ++i) { Stack<int>.Push(i); Stack<double>.Push(1.0/i); } while(Stack<int>.Size != 0) { Console.Write("1/{0} = {1}\n", Stack<int>.Pop(), Stack<double>.Pop()); } } }
- コンパイル & 実行
% mcs GenericClassTest.cs % mono GenericClassTest.exe 1/10 = 0.1 1/9 = 0.111111111111111 1/8 = 0.125 1/7 = 0.142857142857143 1/6 = 0.166666666666667 1/5 = 0.2 1/4 = 0.25 1/3 = 0.333333333333333 1/2 = 0.5 1/1 = 1
制約条件
型引数は<>
で定義されますが、型引数で与えた型に対してwhereキーワードで制約をつけることができる。
色々なパターン
型引数を使って、 他のジェネリッククラスのインスタンス化ができる
class ComplexGenerics { static void Show<Type>(System.Collections.Generic.IList<Type> list) { foreach (Type x in list) Console.Write("{0}\n", x); } static void Main() { int[] a = new int[]{1,2,3,4}; Show(a); double[] b = new double[]{1.0, 2.0 , 3.0}; Show(b); } }
デフォルト値
default(Type) というキーワード で型にあったデフォルト値が得られる
class DefaultGenerics { static void FillWithDefault<T> (T[] array) { for(int i=0; i< array.Length; i++) array[i] = default(T); } static void Main() { int[] a = new int[5]; string[] b = new string [5]; FillWithDefault(a); FillWithDefault(b); foreach(int i in a) Console.Write(i); foreach(string n in b) Console.Write(n); } }