CubicLouve

Spring_MTの技術ブログ

ジェネリックスを少し試す

型だけ違って処理の内容が同じようなものを作るときにオーバーロードさせて書くと、同じコードを書くことになるし、メンテコストも上がる。
なので、色々な型に対応した関数を作ることができる、ジェネリックスを使う。

参照サイト : ジェネリックス

ジェネリックメソッド

例えば、比較のための関数を作る場合、

  • 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);
  }                                                                                                                               
}