SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

C# 1.1からC# 3.0まで~言語仕様の進化

C# 1.1からLINQまで

  • X ポスト
  • このエントリーをはてなブックマークに追加

C# 2.0だと……

 C# 2.0になるとよりよいコードが書けるようになります。まずは型安全性から片づけましょう。

static Employee[] GoldWatch(IEnumerable<Employee> employees) {
  // 可変長配列を用意し、
  List<Employee> results = new List<Employee>();
  // 条件を満たす要素を追加する
  foreach ( Employee employee in employees ) {
    if ( employee.Years > 3 ) {
      results.Add(employee);
    }
  }
  // 配列を取り出す
  return results.ToArray();
}

 C# 2.0で導入されたGenericsにより、シンプルな実装を維持したまま型の安全性を確保できます。上記のコードでは、要素の型がEmployeeでない配列やコレクションを与えると、コンパイル時にエラーとしてくれます。

要素全てを調べ上げてしまう無駄

 さて、まだ問題が残っています。ここまでに挙げたコードはいずれも条件を満たす全ての要素を内包した配列を返しています。が、もし呼び出し側が最初の数個しか必要としないとしたら、条件を満たすものを見つけるべく集合内の要素を全て調べ上げているのはまったくの無駄となります。

 この問題は、C# 2.0で追加された新機能「yield」による列挙を用いて解決します。配列ではなくIEnumerable<Employee>を戻り値とすることで、今までの無駄な列挙をなくすことができます。

static IEnumerable<Employee> GoldWatch(IEnumerable<Employee> employees) {
  foreach ( Employee employee in employees ) {
    // 条件を満たす要素が見つかるたびに
    if ( employee.Years > 3 ) {
      // yieldによってそれを返す
      yield return employee;
    }
  }
}

 これでEmployeeの一時的なコピー領域を排除できました。例えば、呼び出し側で最初の要素だけを必要としていたとき、GoldWatch内では全要素を列挙することがないのでより高速に動作します。

違う項目を検索する場合

 さて、このあとまた同様の検索/列挙が必要となったらどうしましょう? 例えば「営業」に所属する従業員の一覧が欲しくなったとしましょう。おそらくGoldWatchとほとんど同じ、唯一検索条件だけが異なるメソッドSalesForce()を新たに実装することになるのではないでしょうか。そうやって検索条件だけが異なるメソッドを漫然と書き並べるのは効率的とは言い難いものがあります。

 delegateを使って汎用的なFilterメソッドを実装しましょう。

delegate bool Choose(Employee employee);

static IEnumerable<Employee> Filter(
                    IEnumerable<Employee> employees, Choose choose) {
  // employeesの各要素:employeeに対し、
  foreach ( Employee employee in employees ) {
    // choose(employee) が真となるものを返す
    if ( choose(employee) ) {
       yield return employee;
    }
  }
}

// 金時計をもらえる条件
static bool GoldWatchChoose(Employee employee) {
  return employee.Years > 3;
}

// 営業に所属する条件
static bool SalesForceChoose(Employee employee) {
  return employee.Department == "Sales";
}

static IEnumerable<Employee> GoldWatch(IEnumerable<Employee> s) {
  return Filter(employees, new Choose(GoldWatchChoose));
}

static IEnumerable<Employee> SalesForce(IEnumerable<Employee> s) {
  return Filter(employees, new Choose(SalesForceChoose));
}

 このケースでは新たな検索条件が必要となるたびに問い合わせメソッド(GoldWatchSalesForce)およびそれらに対応した条件判断メソッド(GoldWatchChooseSalesForceChoose)の組を追加することになり、保守性の面で芳しくありません。

 そこでC# 2.0の機能、匿名delegateを使ってみましょう。

static IEnumerable<Employee> GoldWatch(IEnumerable<Employee> s) {
  return Filter(employees,
                delegate(Employee employee) { 
                  return employee.Years > 3; 
                }
               );
}

static IEnumerable<Employee> SalesForce(IEnumerable<Employee> s) {
  return Filter(employees,
                delegate(Employee employee) { 
                  return employee.Department == "Sales"; 
                }
                );
}

 匿名delegateによって検索条件を直接書き下すことができるため、GoldWatchChooseSalesForceChooseが不要となりました。

次のページ
C# 3.0登場!

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/1540 2007/08/06 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング