ジェネリックなメソッドやデリゲートがもたらす新スタイル特集C#&VBジェネリック超入門(後編)(4/4 ページ)

» 2006年03月18日 00時00分 公開
[遠藤孝信,デジタルアドバンテージ]
前のページへ 1|2|3|4       

■List<T>クラスのSortメソッド

 ジェネリック・デリゲートをパラメータとして持つ別のメソッドの例として、List<T>クラスのSortメソッドを見てみましょう。

 Sortメソッドは、コレクション内の要素の並べ替えを行います。List<T>オブジェクトに対して、単にSort()メソッドを呼び出すと、コレクションはいわゆる「アイウエオ順」に並べ替えられます。

 このSortメソッドにはいくつかのオーバーライドされたバージョンがあり、ここでは次のバージョンを取り上げます。このSortメソッドのバージョンでは、並べ替えの方法をプログラマーが独自に記述できます。

void Sort(Comparison<T> comparison)

Sub Sort(comparison As Comparison(Of T))

並べ替えの方法を指定できるList<T>クラスのSortメソッド

 Comparison<T>はジェネリック・デリゲートです。その宣言は次のようになっています。

public delegate int Comparison<T>(T x, T y)

Public Delegate Function Comparison(Of T)(x As T, y As T) As Integer

Comparison<T>ジェネリック・デリゲートの宣言

 つまりこの場合、2つのパラメータを取り、int型を返すようなメソッドが必要になります。そのメソッドでは、パラメータで指定された2つの値(xとy)を何らかの基準で比較し、xが大きければ正の値を、yが大きければ負の値を、xとyが等しければ0を返すようにします。

 文章では少し分かりづらいので、次のサンプル・プログラムをご覧ください。このサンプル・プログラムでは、文字列のリストを「文字列の長さ順」に並べ替えます。

using System;
using System.Collections.Generic;

class GenericSample {

  int hikaku(string x, string y) {
    return x.Length - y.Length;
  }

  void Sample() {

    List<string> stringList = new List<string>();
    stringList.Add("各文字列の");
    stringList.Add("長さで");
    stringList.Add("ソートを行います");

    stringList.Sort(hikaku);

    stringList.ForEach(Console.WriteLine);
    // 出力:
    // 長さで
    // 各文字列の
    // ソートを行います
  }

  static void Main() {
    GenericSample gs = new GenericSample();
    gs.Sample();
  }
}

Imports System
Imports System.Collections.Generic

Class GenericSample
  Function hikaku(ByVal x As String, ByVal y As String) As Integer
    Return x.Length - y.Length
  End Function

  Sub Sample()
    Dim stringList As New List(Of String)
    stringList.Add("各文字列の")
    stringList.Add("長さで")
    stringList.Add("ソートを行います")

    stringList.Sort(AddressOf hikaku)

    stringList.ForEach(AddressOf Console.WriteLine)
    ' 出力:
    ' 長さで
    ' 各文字列の
    ' ソートを行います
  End Sub

  Shared Sub Main()
    Dim gs As New GenericSample()
    gs.Sample()
  End Sub
End Class

List<string>を文字列の長さ順に並べ替えるサンプル・プログラム

 List<T>クラスのSortメソッドは、コレクションを並べ替えるのにコレクションから2つの要素を順に取り出し、比較して場所を入れ替えるという作業を繰り返しながら、コレクション全体を並べ替えます。このとき比較に利用されるメソッドが、デリゲートとしてSortメソッドに渡すメソッド(上記プログラムではhikakuメソッド)となります。

 .NETにはデリゲートという機能があるにもかかわらず、これまでのソート(例えば、ArrayListクラスのSortメソッドなど)では、比較を行うメソッドをSortメソッドに渡すために、そのメソッドを実装したオブジェクトを渡す必要がありました。ここにきて、やっとデリゲートが使えるようになったわけです。

【コラム】 C# 2.0の匿名メソッド

 C# 2.0には「匿名メソッド」という機能が導入されています。これは、デリゲートのインスタンスが必要な場所に、いきなりメソッドの中身を記述できるという機能です。

 これまではどのような場合にも、まずメソッドを定義し、そのデリゲートのインスタンスを作成する必要があったのですが、デリゲート経由でしか使うことのない短いメソッドは記述が煩雑になりがちでした。今回解説しているForEachメソッドやSortメソッドもそのようなケースといえるでしょう。

 匿名メソッドを使うと、上記のC#のサンプル・プログラムは次のようになります。

using System;
using System.Collections.Generic;

class GenericSample {

  void Sample() {
    List<string> stringList = new List<string>();
    stringList.Add("各文字列の");
    stringList.Add("長さで");
    stringList.Add("ソートを行います");

    // 匿名メソッドを使用
    stringList.Sort(
      delegate(string x, string y) {
        return x.Length - y.Length;
      }
    );

    stringList.ForEach(Console.WriteLine);
    // 出力:
    // 長さで
    // 各文字列の
    // ソートを行います
  }

  static void Main() {
    GenericSample gs = new GenericSample();
    gs.Sample();
  }
}

匿名メソッドを使用してList<string>のソートを行うサンプル・プログラム

 メソッドのパラメータ部分にコード・ブロックを記述してしまうと少しコードが見にくくなるため、このような匿名メソッドの利用は好みの分かれるところかも知れません。

■ジェネリック・デリゲートとそれを使用するList<T>クラスのメソッド

 最後に、.NET Framework 2.0のクラス・ライブラリで定義されている全ジェネリック・デリゲートを次に列挙しておきます。すべてSystem名前空間で定義されています(VB版は省略)。

  • delegate void Action<T>(T obj)
  • delegate int Comparison<T>(T x, T y)
  • delegate bool Predicate<T>(T obj)
  • delegate TOutput Converter<TInput, TOutput>(TInput input)
  • delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs: EventArgs

 最後のEventHandlerデリゲートからも分かるように、ジェネリック・クラスやジェネリック・メソッドと同様に、ジェネリック・デリゲートにも制約を記述可能です。

 そして次の一覧は、List<T>クラスで定義されている、ジェネリック・デリゲートをパラメータに取るメソッドの一覧です(オーバーロードは除く)。

  • void ForEach(Action<T> action)
  • void Sort(Comparison<T> comparison)
  • bool Exists(Predicate<T> match)
  • bool TrueForAll(Predicate<T> match)
  • int RemoveAll(Predicate<T> match)
  • List<T> FindAll(Predicate<T> match)
  • int FindIndex(Predicate<T> match)
  • int FindLastIndex(Predicate<T> match)
  • T Find(Predicate<T> match)
  • T FindLast(Predicate<T> match)
  • List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)

 最後のConvertAllメソッドはジェネリック・メソッドとなっています。一見複雑そうに見えますが、ここまでに解説した内容を理解していれば、コードを記述するのは難しくないはずです。ジェネリックを活用してC#&VBのプログラミングをより楽しみましょう。

「特集C#&VBジェネリック超入門」のインデックス

特集C#&VBジェネリック超入門

前のページへ 1|2|3|4       

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。