はじめに
コードネーム「Orcas」で知られる次期Visual Studioは、Visual Studio 2008と命名され、今年の末にリリースされることになったそうです。2000年あたりからじわじわと浸透してきたC#はVisual Studio 2008でさらにバージョンアップし、C# 3.0となります。「Orcas」のβリリースで新機能を試していらっしゃる方も少なくないでしょう。
本稿では、C# 1.1から3.0までの言語仕様の進化の一例をたどり、言語の進化がコーディング・スタイルに及ぼす影響を考えます。
対象読者
C#が大好きな方、およびよりエレガントなコードを志向する駆け出し(?)プログラマ。
C#、LINQ and Whatnot
Microsoft社員がユーザーに向けて発信している MSDN blogに面白い記事を見つけました。
Jomo Fisher氏のblogではC#にまつわるさまざまなトピックを解説してくれているのですが、その中の1つに以下のものがあります。
2005年9月の記事であることからも決して最新情報ではありません。ですが、この記事は筆者にとって、とてもためになりました。C# 1.1で書かれた1つのコードがC#のバージョンが上がるにつれて次第に磨かれていく様が活き活きと描かれています。ぜひ原文を読んでもらいたいのですが、英語が苦手な方のために、その概要を紹介します。
金時計をもらえる人は……?
ここに従業員の情報を集めたクラスEmployee
があります。
// 従業員 class Employee { public string Name; // 名前 public int Years; // 勤続年数 public string Department; // 部署 }
さて、会社は勤続年数が3年を超える社員に記念の金時計を贈ることにしました。そこでこのEmployee
配列の中から金時計をもらえる社員をリストアップしなければなりません。
C# 1.1では……
C# 1.1ならこんなコードで実現することでしょう。
// 金時計をもらえる社員のリストアップ static Employee[] GoldWatch(Employee[] employees) { // 条件を満たす社員数を求める int resultCount = 0; for ( int i = 0; i < employees.Length; ++i ) { if ( employees[i].Years > 3 ) { ++resultCount; } } // 必要なサイズの配列を用意する Employee[] results = new Employee[resultCount]; // 条件を満たす社員を配列にコピー for ( int i = 0, j = 0; i < employees.Length; ++i ) { if ( employees[i].Years > 3 ) { results[j] = employees[i]; ++j; } } return results; }
メソッドGoldWatch
では、まず条件を満たす要素の数を求め、必要なサイズの配列を確保し、そしてその配列の中身を埋めています。
確かにこれでも動作しますがエレガントとは言い難いです。employees
は2度もスキャンされていますし、条件判断Years>3
も2度評価されています。こんな「条件を満たす要素からなる部分集合を作る」という簡単な処理にしては、いささかややこしいコードです。
より簡単に実装した例は、以下のようになります。
static Employee[] GoldWatch(Employee[] employees) { // 可変長配列を用意し、 ArrayList results = new ArrayList(); // 条件を満たす要素を追加する for (int i = 0; i < employees.Length; ++i) { if (employees[i].Years > 3) { results.Add(employees[i]); } } // 配列を取り出す return (Employee[])results.ToArray(typeof(Employee)); }
.NET Frameworkライブラリが提供する可変長配列ArrayList
を使うことで要素数を求めなくてもよくなりました。その一方で型に対する安全性が犠牲になっています。可変長配列ArrayList
はobject
を要素とするためです。そのため最後に複雑なキャストを行っています。
これら2つのアプローチはいずれも柔軟性に欠けています。引数employees
は配列でなければなりません。ArrayList
を引数とする同様のメソッドを追加することはできますが、さらにまた別の型をサポートしたくなるかもしれません。これでは、きりがありませんね。
そこでまた別のアプローチが考えられます。
static Employee[] GoldWatch(IEnumerable employees) { // 可変長配列を用意し、 ArrayList results = new ArrayList(); // 条件を満たす要素を追加する foreach ( Employee employee in employees ) { if ( employee.Years > 3 ) { results.Add(employee); } } // 配列を取り出す return (Employee[])results.ToArray(typeof(Employee)); }
このコードはEmployee
の配列のみならずArrayList
のようなコレクションも引数として与えることができます。ただし型に対しては安全性が犠牲になってます。引数に与えられたIEnumerable
が列挙してくれるのがはたして本当にEmployee
であるか、コンパイル時には判断できないためです。
C# 1.1でできるのはここまででしょう。