■List<T>クラスのSortメソッド
ジェネリック・デリゲートをパラメータとして持つ別のメソッドの例として、List<T>クラスのSortメソッドを見てみましょう。
Sortメソッドは、コレクション内の要素の並べ替えを行います。List<T>オブジェクトに対して、単にSort()メソッドを呼び出すと、コレクションはいわゆる「アイウエオ順」に並べ替えられます。
このSortメソッドにはいくつかのオーバーライドされたバージョンがあり、ここでは次のバージョンを取り上げます。このSortメソッドのバージョンでは、並べ替えの方法をプログラマーが独自に記述できます。
void Sort(Comparison<T> comparison)
Sub Sort(comparison As Comparison(Of T))
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
つまりこの場合、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<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<T>クラスのメソッド
最後に、.NET Framework 2.0のクラス・ライブラリで定義されている全ジェネリック・デリゲートを次に列挙しておきます。すべてSystem名前空間で定義されています(VB版は省略)。
最後のEventHandlerデリゲートからも分かるように、ジェネリック・クラスやジェネリック・メソッドと同様に、ジェネリック・デリゲートにも制約を記述可能です。
そして次の一覧は、List<T>クラスで定義されている、ジェネリック・デリゲートをパラメータに取るメソッドの一覧です(オーバーロードは除く)。
最後のConvertAllメソッドはジェネリック・メソッドとなっています。一見複雑そうに見えますが、ここまでに解説した内容を理解していれば、コードを記述するのは難しくないはずです。ジェネリックを活用してC#&VBのプログラミングをより楽しみましょう。
Copyright© Digital Advantage Corp. All Rights Reserved.