特集

C# 2.0新機能徹底解説(前編)

開発生産性を飛躍的に高めるジェネリック

菊池 和彦
Microsoft MVP Visual Developer - Visual C#)
2004/11/23
Page1 Page2 Page3 Page4

■ジェネリック・コレクションの利用

 以下ではサンプル・コードを多用してジェネリックを説明していきたいと思う。作成するプログラムは受注管理アプリケーションだ。注文を示すPurchaseOrder型の扱いが主題となる。

 PurchaseOrder型のデータは「注文」である。よってそのクラスの定義には、製品と個数のリスト、注文主を示す情報を持っている。

class PurchaseOrder
{
  struct OrderLine        // 注文の明細
  {
    public Product prod;  // 製品
    public int amount;    // 個数
    public OrderLine( Product prod,int amount )
    {
      this.prod=prod;
      this.amount=amount;
    }
  };
  List<OrderLine> Items;  // 明細のリスト
  Customer customer;      // 注文主
  DateTime OrderDate;     // 注文日時
}
注文を示すPurchaseOrderクラスの定義

 List<OrderLine> Itemsという行はOrderLine構造体に特化した「List<T>型」のフィールドを宣言するコードである。1つのOrderLine構造体は、製品と個数を組み合わせた、注文の明細行に相当する。

 ここで、List<T>クラス(System.Collections.Generic名前空間)はジェネリックにより実装されたコレクションの一種であり、データ(この場合は、OrderLine構造体)の保持に使われる。

 簡単にいうと、List<T>クラスは.NET Framework 1.1にもあるArrayListクラス(System.Collections名前空間)のジェネリック対応版と考えればよいだろう。コレクションは、次のような機能を持つことが一般的だ。

  • コレクションの全要素に対して処理を行うための「巡回」
  • 条件に合うデータの「検索・抽出」や「削除」
  • 条件に合うデータの「存在確認」や、全要素が条件を満たすかの「判定確認」
  • 別のデータ形式への「変換」
  • 指定された順序に従った「並べ替え」

 これらのすべての機能は、ジェネリックにより汎用アルゴリズムとしてList<T>クラスに実装されている。例えば次のようなメンバが存在する。

機能 Listクラスのメンバ
巡回 ForEachメソッド
検索・抽出 FindIndexメソッドFindAllメソッドなど
削除 RemoveAllメソッドなど
存在確認 Existsメソッド
判定確認 TrueForAllメソッド
変換 ConvertAllメソッド
並べ替え Sortメソッド
コレクションのための汎用アルゴリズムを実装しているList<T>クラスのメンバ(メソッド)の一部

 このようなメンバを利用することで、具体的なアルゴリズムは一切実装することなく、コレクションとしての基本機能を利用することができる。なお、上記したもの以外のList<T>クラスのメンバについてはコードに出てくるたびに解説したりしないので、より詳しく知りたい場合には、次のページで確認してほしい。このページは、Visual Studio 2005ベータ版のドキュメント・サイトのもので、そこでは.NET Framework 2.0のクラス・ライブラリやC# 2.0、Visual Basic .NET 2.0などのリファレンス・マニュアルが公開されている。

 それでは、これらのジェネリックにより実装されたコレクションであるList<T>クラスの使用例を以下に示していこう。

■コレクションへのデータの追加:Addメソッド

 まずは準備として、明細に1行(つまりコレクションに1データ)を追加してみよう。これを行う処理は以下のようなコードになる。

public void AddOrderLine( Product prod,int amount )
{
  Items.Add( new OrderLine(prod,amount) );
}
ジェネリックを使ったList<T>コレクションへのデータの追加

 この時点ですでにジェネリックによる恩恵を受けている。コード上には表れないが、このコードにより、List<T>コレクション(=Itemsコレクション)にObject型のデータでなくOrderLine型のデータが格納されるのである。従来の.NET Framework 1.xのコレクション・クラスであれば、同様のコードではObject型としてデータを格納しなければならなかった。

■データの巡回:ForEachメソッドとAction<T>デリゲート

 次に、明細の合計金額を得る処理について考えてみよう。単純に書けば以下のようになる。

public decimal TotalPrice
{
  get {
    decimal total =0;
    foreach( OrderLine ol in Items )
    {
      total += ol.prod.Price * ol.amount;
    }
    return total;
  }
}
明細の合計金額を取得するTotalPriceプロパティ

 ジェネリックによるコーディングのメリットの1つは、List<T>コレクションがOrderLine構造体データをそのまま処理できるようになるので、キャスト(型変換)が不要であることだ。このように、OrderLine構造体のために新たなコレクション・クラスを自分で作る必要がないことは非常に便利だ。

 しかも、上記のコードはジェネリック以前のforeachを利用したコードと何も変わらない。よってこのコードでは、ジェネリックの存在はまったく気にする必要がない。しかしここで、気にする必要のないことをあえて気にしてみよう。

 ジェネリックでコレクション内の全データに対して一定の処理を行うには、List<T>クラスのForEachメソッドとAction<T>デリゲートを利用する。ForEachメソッドはAction<T>デリゲートをパラメータに取り、各要素をAction<T>デリゲートに渡してくれる(詳しくは「VS 2005 Beta Library : List<T>.ForEach Method」を参照されたい)。

 Action<T>デリゲートの処理としては、OrderLine構造体データごとの価格を計算し、それらを加算して合計額を算出するという処理を記述する。ちなみにAction<T>デリゲートは次のように宣言されている。

public sealed delegate void Action<T>(T obj);

 まずはAction<T>デリゲートを記述し、それを使うように先ほどのTotalPriceプロパティのGetアクセサを書き換えてみよう。

class SumTotalPrice
{
  public decimal Value;
  // Action<T>デリゲートの処理を実装
  public void Action( OrderLine ol )
  {
    Value += ol.prod.Price * ol.amount;
  }
}

public decimal TotalPrice
{
  get {
    SumTotalPrice sum = new SumTotalPrice();
    // ForEachメソッドにより合計金額を算出
    Items.ForEach( sum.Action );
    return sum.Value;
  }
}
ForEachメソッドとAction<T>デリゲートを利用して処理の巡回を行うサンプル・コード

 このサンプル・コードではForEachメソッドのパラメータに(明示的なデリゲートの生成を行わずに)デリゲートのメソッド名をそのまま指定しているが、このような記述は.NET Framework 2.0から追加された新規の言語機能だ(具体的な機能名は不明だが、「Delegate Inference(デリゲート・インフェレンス)」と呼ばれることがある)。

 この機能は、デリゲートが必要な場面でメソッド名をそのまま記述すると自動的にデリゲートを生成してくれる。.NET Framework 1.1までであれば

Items.ForEach( new Action<OrderLine>( sum.Action ) );

のように記述して、明示的にデリゲートを生成する必要があったが、2.0では非常に簡単に記述できるにようになっている。


 INDEX
  [特集] C# 2.0新機能徹底解説(前編)
  開発生産性を飛躍的に高めるジェネリック
    1.ジェネリックとは?
  2.ジェネリック・コレクションの利用
    3.汎用アルゴリズムを実装しているList<T>クラスのメンバ
    4.ジェネリックを使ったコレクション・クラスの拡張
  [特集] C# 2.0新機能徹底解説(後編)
  進化したC# 2.0の状態管理、匿名メソッドとイテレータ
    1.匿名メソッドとその正体
    2.イテレータの衝撃!
    3.イテレータを解剖する
    4.確実な後処理
 


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間