C#再入門 〜その1.ジェネリックス

VisualStudio2010になりC#のバージョンも4.0になりました。度重なるバージョンアップによってC#は多機能になり、様々なプログラミングパラダイムを取り入れ、成長してきました。

C#の機能をもう一度見直そうということで、機能ごとにピックアップして説明していこうかと思います。

ジェネリック

今回はジェネリックスの紹介を行ないます。

C#1.0の頃

C#1.0のころ、可変長配列やハッシュテーブルを使う場合、要素はobject型として扱わなければならず、要素を取り出す際にキャストの必要性がありました。また、想定外の型のオブジェクトを追加してもコンパイルエラーは出てくれません。

using System;
using System.Collections;
namespace GenericsSample{
  class Program{
    static void Main(string[] args){
      ArrayList list = new ArrayList();
      list.Add(1);
      list.Add(5);
      list.Add(8);
      int x = (int)list[1];//ここでキャスト

      list.Add("hello");//文字列を代入してもエラー出ない
      int y = (int)list[3];//ランタイムエラー
    }
  }
}

キャストは手間がかかりますし、キャストが失敗する可能性も含め実装しなければなりません。

C#2.0の頃

それを解決するため、ジェネリックスという機能が追加されました。

using System;
using System.Collections.Generic;
namespace GenericsSample{
  class Program{
    static void Main(string[] args){
      IList<int> list = new List<int>();
      list.Add(1);
      list.Add(5);
      list.Add(8);
      int x = list[1];//キャストの必要性なし
      list.Add("hello");//ここでコンパイルエラー
    }
  }
}

IListやListなどがジェネリッククラスと呼ばれるもので、Tに型を入れることによって、コンパイラにListの要素の型を押しててあげることが出来ます。Listならintのリスト、Listならstringのリスト、といった感じです。

ジェネリッククラスを使うことにより、コンパイラによる要素の型チェックが行われるようになり、間違った型のオブジェクトを代入しようとした場合コンパイルエラーになります。

コンパイラさんがきちんとチェックしてくれるので、キャストによる実行時エラーに怯える心配もなくなります。

ListなどのクラスはSystem.Collections.Generic名前空間に含まれてますので使用する場合は「using System.Collections.Generic」が必要になります。

ジェネリッククラスの作成

ジェネリッククラスや、ジェネリックメソッドは使うだけでなく自分で定義することもできます。例としてスタッククラスを作ってみました。

class Stack<T>{
  private LinkedList<T> source;
  public Stack(){
    source = new LinkedList<T>();
  }
  public void Push(T item){
    source.AddLast(item);
  }
  public T Pop(){
    var result = source.Last;
    source.RemoveLast();
    return result.Value;
  }
}

Tというのが型パラメータです。使い方はIListやListと同じように、型を指定してあげます。
例えばStackとした場合、型パラメータTにstringが入り、Push()の引数、Pop()の戻り値の型はstringになり、内部で使用しているLinkedListの型もLinkedList型になります。

static void Main(string[] args){
  Stack<string> strs = new Stack<string>();//stringのスタック
  strs.Push("abc");
  strs.Push("hello");
  strs.Push("foo");
  Console.WriteLine(strs.Pop()); // foo

  Stack<int> nums = new Stack<int>();//intのスタック
  nums.Push(3);
  nums.Push(4);
  nums.Push(5);
  Console.WriteLine(nums.Pop());// 5
}

また、型パラメータにはwhereで厳密な制約を出来たり、C#4.0から共変、反変を指定できるようになり、使いこなせば非常に心強い機能です。


次はコレクションクラスの説明を行いたいと思います。