C# 3.0登場!
C# 3.0の新機能、ラムダ式を使うと匿名delegateを簡潔に表現できます。先ほどの匿名delegateをラムダ式で書き換えると以下のようになります。
static IEnumerable<Employee> GoldWatch( IEnumerable<Employee> employees) { return Filter(employees, employee => employee.Years > 3 ); } static IEnumerable<Employee> SalesForce( IEnumerable<Employee> employees) { return Filter(employees, employee => employee.Department == "Sales" ); }
GoldWatch
、SalesForce
の利用者コードは、
GoldWatch(employees); SalesForce(employees);
のようになりますね。一方オブジェクト指向世界でのメソッド呼び出しは、
employees.GoldWatch(); employees.SalesForce();
のように名詞.動詞()
の形式となります。
C# 3.0では「拡張メソッド」が新機能として追加されます。これによって既存のクラスに変更を加えることなく新たにメソッドを追加できます。
static IEnumerable<Employee> Filter( this IEnumerable<Employee> employees, Choose choose) { foreach ( Employee employee in employees ) { if ( choose(employee) ) { yield return employee; } } } static IEnumerable<Employee> GoldWatch( this IEnumerable<Employee> employees) { return employees.Filter(employee => employee.Years>3); } static IEnumerable<Employee> SalesForce( this IEnumerable<Employee> employees) { return employees.Filter(employee => employee.Department=="Sales"); }
各staticメソッドの第1引数IEnumerable<Employee>
に先立ってthis
が付いてますね。これが拡張メソッドの印です。この形式でstaticメソッドを定義すれば、IEnumerable<Employee>
にあたかもメソッドが追加されたかのように動作します。
employees.GoldWatch();
employees.SalesForce();
employees.Filter(employee => employee.Department=="Sales");
さらに、拡張メソッドGoldWatch
、SalesForce
はIEnumerable<Employee>
を返すので、メソッドを数珠つなぎにできます。
employees .GoldWatch() .SalesForce();
さらなる汎用化を考えましょう。Employee
の集合であるIEnumerable<Employee>
に拡張メソッドFilter
を適用することで、あらゆる条件での選択操作が可能になりました。同様に顧客Customer
や棚卸Inventry
にも同様のFilter
が欲しくなります。それらを個々に追加するのではなく拡張メソッドFilter
を汎用化します。
delegate bool Choose<T>(T t); static IEnumerable<T> Filter<T>(this IEnumerable<T> items, Choose<T> choose) { foreach ( T item in items ) { if ( choose(item) ) { yield return item; } } }
今までEmployee
であったのをGenericパラメータT
に書き換えました。こうすることで任意の型の集合に対しFilter
を適用できます。
int [] a = new int [] {1,2,3,4,5}; a.Filter(i => i==1 || i==3);
まとめ
Visual Studioには3つの.NET言語「VB.NET」「C#」「C++/CLI」が用意されています。それぞれのコンパイラはいずれも中間コード(IL)を生成するので実行時のパフォーマンスに大差はありません。プログラマは目的や好み/慣れに応じて言語を選ぶことができます。
筆者は長いことC++世界に暮らしているので.NET言語としてはC++/CLIを選ぶことが多いのですが、C#の表現力はVB.NETやC++/CLIより一歩先を行っているように感じられます。本稿で紹介したyieldイテレータ、匿名delegate、そして近未来のラムダ式と拡張メソッドはC++屋を嫉妬させるエレガンスがありますね。