List 14-8の例は、ArrayListに格納されるインスタンス自身が、どう並べ替えられるべきかを知っている場合のサンプルである。しかし、ソート順がいつも決まっている場合ばかりとは限らない。例えば、住所録なら、名前順でソートしたいときや、住所順でソートしたいときがあるだろう。そういうケースでは、ソート順をクラスの外部で指定するという方法もある。この方法なら、さまざまなソート方法を準備しておき、それを切り替えながら利用できる。List 14-9はそれを実現した例である。
1: using System;
2: using System.Collections;
3:
4: namespace Sample009
5: {
6: class ClassComparer : IComparer
7: {
8: public int Compare( object x, object y )
9: {
10: double dx = (double)x;
11: double dy = (double)y;
12: return (int)dx - (int)dy;
13: }
14: }
15: class Class1
16: {
17: [STAThread]
18: static void Main(string[] args)
19: {
20: ArrayList al = new ArrayList();
21: al.Add( 2.2 );
22: al.Add( 3.7 );
23: al.Add( 1.5 );
24: al.Add( 2.7 );
25: al.Add( 3.2 );
26: al.Add( 1.1 );
27: al.Sort( new ClassComparer() );
28: foreach( double d in al )
29: {
30: Console.WriteLine( d );
31: }
32: }
33: }
34: }
これを実行するとFig.14-9のようになる。
List 14-9のサンプルでは、ちょっと変わった条件付けを行ってソートを行っている。つまり、実数をソートするのだが、順番は整数扱いで決めるというものだ。小数部は昇順に揃っていないが、整数だけは揃っているという変な状態を作り出している。これを実現するには、Sortメソッドの引数にIComparerインターフェイスの参照を渡す。27行目にその実例が記述されている。ここではClassComparerクラスのインスタンスが渡されているが、これにはIComparerが実装されているので、自動的にそれが取り出されて渡される。
このIComparerは実装されなければ使えないので、IComparerを実装するクラスとして、6〜14行目にClassComparerクラスを定義している。IComparerは1つのメソッドCompareを含んでいるので、これを実装する。8行目の引数のx、yは、比較すべき2つのオブジェクトである。ここでは、実数がくるはずなので、10〜11行目のように、一度、doubleにキャストしてやる。そして、12行目で整数にキャストして比較する。この比較により、小さければ負数、同じなら0、大きければ正数を返すという条件が満たされる。
以上により、27行目のSortメソッド実行時には、何度もCompareメソッドが呼ばれ、その結果に並べ替えが行われるというわけである。
.NET Frameworkでは、どんなクラスもToStringというメソッドを持っている。どんなオブジェクトも、文字列に変換される必要がある場合、これが呼び出される。例えば、Console.WriteLineメソッドなどで出力するときに、自作クラスを指定しても、ToStringメソッドさえきちんと実装されていれば、適切な値が出てくるわけである。ここまでの機能は、単なる継承によって実現される。だが、インターフェイスを使うと、もうちょっと凝ったことができる。
デフォルトのカルチャーが日本の日本語(ja-JP)である場合に、数値の1、2、3を、漢数字の一、二、三にする機能を記述してみよう。List 14-10はそれを実現する例である。
1: using System;
2: using System.Globalization;
3:
4: namespace Sample010
5: {
6: class ClassSample : IFormattable
7: {
8: public int x;
9: public string ToString( string format, IFormatProvider formatProvider )
10: {
11: if( formatProvider is CultureInfo )
12: {
13: CultureInfo ci = (CultureInfo)formatProvider;
14: if( ci.Name == "ja-JP" )
15: {
16: switch( x )
17: {
18: case 1:
19: return "一";
20: case 2:
21: return "二";
22: case 3:
23: return "三";
24: }
25: }
26: }
27: return x.ToString();
28: }
29: }
30: class Class1
31: {
32: [STAThread]
33: static void Main(string[] args)
34: {
35: ClassSample cs = new ClassSample();
36: cs.x = 3;
37: Console.WriteLine( cs );
38: }
39: }
40: }
これを実行するとFig.14-10のようになる。
では解説しよう。IFormattableとは、インスタンスを文字列で書式化する機能を提供するインターフェイスである。カルチャーの情報などを参照して、動作環境によって動作を変えることができる。例えば、日付などの処理に利用されるものである。アメリカなら「January」だが、日本では「一月」を出力する、というような場面で意味を持つ。
IFormattableが持つメソッドはToStringだけである。このToStringは、すべてのクラスが持っているToStringとは異なり、9行目にあるとおり、2つの引数を持つ。最初の引数には、通貨などの条件を指定する文字列がくるが、ここでは無視している。2番目の引数には、Console.WriteLineメソッドなどから呼び出すときはカルチャー情報が渡されてくる。そこで、11行目で確認してから、13行目でキャストしてCultureInfoクラスのインスタンスとして扱う。このインスタンスのNameプロパティの値が「ja-JP」なら日本語環境で動いていることを示す。これらの条件を満たすときには、16〜24行目のように、数値の1〜3を漢数字として返している。どの条件にも当てはまらない場合は、27行目のように、引数のないToStringで文字列に変換しているが、これは、数値の123が文字列の「123」になるような単純な変換である。
さて、これらのコードによって、37行目のように直接インスタンスをConsole.WriteLineに渡すと、Console.WriteLineメソッドの書式整形処理の一環として、9行目からのToStringメソッドが呼び出されるので、出力内容をカルチャー情報によって変化させることができるのである。
『新プログラミング環境 C#がわかる+使える』
本記事は、(株)技術評論社が発行する書籍『新プログラミング環境 C#がわかる+使える』から許可を得て一部分を転載したものです。
【本連載と書籍の関係について 】
この書籍は、本フォーラムで連載した「C#入門」を大幅に加筆修正し、発行されたものです。連載時はベータ版のVS.NETをベースとしていましたが、書籍ではVS.NET製品版を使ってプログラムの検証などが実施されています。技術評論社、および著者である川俣晶氏のご好意により、書籍の内容を本フォーラムの連載記事として掲載させていただけることになりました。
→技術評論社の解説ページ
ご注文はこちらから
Copyright© Digital Advantage Corp. All Rights Reserved.