ヒント3:.NETのジェネリックによる並べ替え
Listジェネリッククラスには、並べ替え処理と検索処理を行うためのメソッドがあります。また、独自の並べ替えや検索のロジックを実装することもできます。それぞれ見てみることにしましょう。
次のコードは、選手名による並べ替えと、チームごとの選手名による並べ替えの例です。いずれも、「匿名メソッド」というC#の新しい言語機能を使用して、並べ替えのロジックをインラインで指定しています。
private void TestSortBaseballClass() { List<BaseballClass> oBaseball = new List<BaseballClass>(); oBaseball = this.PopulateBaseballClass(); string cResults = ""; // Anonymous delegate to sort on name oBaseball.Sort (delegate(BaseballClass f1, BaseballClass f2) { return f1.Name.CompareTo(f2.Name); } // Anonymous delegate to sort on Team, then Name oBaseball.Sort (delegate(BaseballClass f1, BaseballClass f2) { return f1.Name.Insert(0, f1.Team).CompareTo( f2.Name.Insert(0, f2.Team)); } }
匿名メソッドを使用すると、コードブロックをデリゲートにカプセル化でき、通常ならメソッド名を指定する部分に、インラインでコードを記述できます。メソッドを別途作成する必要がないため、デリゲートをインスタンス化するときのコーディングの手間を減らすことができます。デリゲートの代わりにコードブロックを指定する方法は、メソッドを作成するのが無駄な手間に感じられる場合に有効です。
匿名メソッドの構文は、最初は扱いづらいように思えるかもしれません。しかし次の例では、匿名メソッドを使うことによって、クラスを余分に追加する手間が省けます。
// In-line anonymous method that compares 2 // incoming values oBaseball.Sort(delegate(BaseballClass f1, BaseballClass f2) { return f1.Name.CompareTo(f2.Name); }); // A second anonymous method, inserts team // in front of name, to sort on two columns oBaseball.Sort(delegate(BaseballClass f1, BaseballClass f2) { return f1.Name.Insert(0, f1.Team).CompareTo( f2.Name.Insert(0, f2.Team)); });
ヒント4:.NETのジェネリックでの検索と匿名メソッド(オプション)
.NETのListジェネリッククラスには、検索を実行するためのメソッドとして、Find、FindAll、FindLastがあります。次の例のように、独自の検索ロジックをインラインで実装する匿名メソッドを作成することもできますし、Find系メソッドのPredicate(ブール値関数)となるクラスを作成することもできます(リスト1とリスト2を参照)。
private void TestFindBaseballClass() { List<BaseballClass> oBaseball = new List<BaseballClass>(); oBaseball = this.PopulateBaseballClass(); string cSearch = "Astros"; BaseballClass oSingleFind = oBaseball.Find( delegate(BaseballClass f1) { return f1.Team == cSearch; }); MessageBox.Show("Single find is " + oSingleFind); string cResults = ""; foreach (BaseballClass oRecord in oBaseball.FindAll( (delegate(BaseballClass f1) { return f1.Team == cSearch; }))) cResults += oRecord + "\r\n"; MessageBox.Show("Multiple find is " + oSingleFind); }
上のコードでは、その前の例と同じ手法を使っています。つまり、oBaseballの中身を検索して条件に一致する最初のインスタンスを探すデリゲートをインラインメソッドで作成しています。仮にここで、完全一致ではなく部分文字列による検索を実装するとしたら、匿名メソッド内で別の文字列関数を使用することも可能です。
ListクラスのFindAllメソッドについても、同様の方法で使用できます。インラインのデリゲートと匿名メソッドを指定し、受け取った各BaseballClassオブジェクトで検索文字列に一致するものをブール値で返しています。FindAllメソッドでは、ADO.NETのDataTableオブジェクトのようなフィルタ機能を実装できます。
string cSearch = "Astros"; // Perform a single search on the first hit BaseballClass oSingleFind = oBaseball.Find( delegate(BaseballClass f1) { return f1.Team == cSearch; }); // Perform a find all (almost like a filter) string cResults = ""; foreach (BaseballClass oRecord in oBaseball.FindAll((delegate(BaseballClass f1) { return f1.Team == cSearch; }))) cResults += oRecord + "\r\n";
独立したクラスとして作成したい場合(あるいはVisual Studio 2005で匿名メソッドをサポートしていないVB.NETを使用している場合)ために、独立したFindクラスの例とその使用例をリスト1とリスト2に示しておきます。
using System; using System.Collections.Generic; using System.Text; namespace GenericsDemo { class FindBaseballClass { private string team; public FindBaseballClass(string team) { this.team = team; } // Implement specific search logic - return true or false public bool TeamFind(BaseballClass oBaseball) { return oBaseball.Team == team; } } }
private void TestFindBaseballClass() { List<BaseballClass> oBaseball = new List<BaseballClass>(); oBaseball = this.PopulateBaseballClass(); FindBaseballClass oFind = new FindBaseballClass("Astros"); BaseballClass oSingleFind = oBaseball.Find( new Predicate<BaseballClass>(oFind.TeamFind)); MessageBox.Show("Single find is " + oSingleFind); string cResults = ""; foreach (BaseballClass oRecord in oBaseball.FindAll( new Predicate<BaseballClass>(oFind.TeamFind))) cResults += oRecord + "\r\n"; MessageBox.Show("Multiple find is " + oSingleFind); }
ヒント5:Listクラスへの他オブジェクトの格納
前述のとおり、新しいListクラスには、.NETのどんな型でも格納できます。リスト3は、特定のインターフェイスを実装するクラスを格納する例です。たとえば、複数のレポートクラスがあり、それらはすべてGenerateDocumentというメソッドを持つとします。次のような処理を行うことで、メインの制御ループ構造を作成できます。
GenerateDocumentという1つのメソッドを持つ、IDocumentというインターフェイスを定義します。IDocumentを実装する複数のレポートドキュメントクラス(SimpleClass1、SimpleClass2など)を作成します。IDocumentインターフェイスを実装しているさまざまなクラスを格納するためのListオブジェクトのインスタンスを作成します。- レポートドキュメントクラス(
SimpleClass1など)のインスタンスを作成し、リストにインスタンスを追加します。 - リストを反復処理し、
GenerateDocumentメソッドを呼び出します。
List<SimpleInterface.IDocument> oList =
new List<SimpleInterface.IDocument>();
foreach(SimpleInterface.IDocument oClass in oList) oClass.GenerateDocument();
この例については、GE HealthcareのRob Haleに感謝します。Robは、Boston CodeCampで私が開催したC# 2.0セッションの参加者で、このコードのアイデアの発案者です。本当にありがとう、Rob!
public class SimpleInterface { public interface IDocument { void GenerateDocument (); } } class SimpleClass1 : SimpleInterface.IDocument { public SimpleClass1() { } public void GenerateDocument() { // Method code goes here! } } class SimpleClass2 : SimpleInterface.IDocument { public SimpleClass2() { } public void GenerateDocument() { // Method code goes here! } } List<SimpleInterface.IDocument> oList = new List<SimpleInterface.IDocument>(); SimpleClass1 oClass1 = new SimpleClass1(); SimpleClass2 oClass2 = new SimpleClass2(); oList.Add(oClass1); oList.Add(oClass2); foreach (SimpleInterface.IDocument oClass in oList) oClass.GenerateDocument();
