特集

C# 2.0新機能徹底解説(前編)

開発生産性を飛躍的に高めるジェネリック

菊池 和彦
Microsoft MVP Visual Developer - Visual C#)
2004/11/23
Page1 Page2 Page3 Page4

 C# 2.0(2005年提供予定)の新機能徹底解説ということで、数回に分けてC# 2.0の拡張点に関する記事を執筆させていただくことになった。C# 2.0の主要な改善点は、一言で表現すれば、「より書きやすくなった」ことだ。C言語に源流を持つ言語(例えば、C++やJavaなど)の根底にある思想は「シンプルなコードは美しい」ということだが、C# 2.0でもこの考え方が受け継がれている。C# 2.0はシンプルかつパワフルな言語である。

Whidbeyの新機能(特集:.NET開発者のためのPDC 2003レポート)

 C# 2.0では、現行のC# 1.0に対していくつかの新機能が追加されているが、その中でも主要なものは以下の3点だ。

  • ジェネリック(Generics)
  • 匿名メソッド(Anonymous Methods)
  • イテレータ(Iterator)

 今回では、このうちジェネリックについて解説する。

 ジェネリックについては、まず「ジェネリックとは何か?」を説明し、さらにジェネリックを使ううえで想定される問題点を取り上げる。次に、実際にジェネリックを使ったサンプル・プログラムを作成しながらその実装方法について解説する。

ジェネリックとは?

 ジェネリックは「汎用プログラミング」というプログラミング手法を取り込むための言語機能である。

 汎用プログラミングとは、実装済みの汎用的なアルゴリズムを組み合わせてプログラムを記述することで、プログラマが同じアルゴリズムを何度も記述する必要をなくすためのプログラミング手法だ。これにより、プログラマは問題領域(アルゴリズムなどの細かな実装レベルの領域ではなく、実際の業務レベルの領域)により近い部分の開発に集中できるようになる。

 オブジェクト指向プログラミングでは、物事(=オブジェクト)を抽象クラスである基底クラスから派生/継承して実現(=生成)することに主眼が置かれている。このため、クラス(型)が枝分かれして分類されるので、例えばオブジェクト同士を比較する方法などはそれぞれのクラスごとに異なってくることになり、抽出やソートなどのアルゴリズムが確立された汎用的なコードをクラスごとに何度も記述する必要が出てくる。このように確立済みのソート・アルゴリズムの移植を繰り返すことによって、バグという名の突然変異が生まれてくることは少なくない。

オブジェクト指向プログラミングでバグが発生するメカニズム
オブジェクト指向プログラミングでは、クラスごとに同じアルゴリズムを何度も記述する必要があるため、そのアルゴリズムの移植作業でバグが混入する可能性が発生する。

 この問題点は、型(クラス)によらずに適用可能なアルゴリズムが、オブジェクト指向プログラミングの持つ「型を分類する」という考え方によって、「そのまま」では適用不可能となってしまうことに原因がある。

 よってコンパイラや実行環境に「そのまま」ではなく、型に応じた変化を受け入れる仕組みがあればその問題が解決されることになる。このような仕組みがあれば、汎用アルゴリズムを何度も記述する必要はなく、1度だけ記述すればよい。後は型による変化をコンパイラや実行環境が受け入れてアルゴリズムを適用してくれる。さらには、その1度目を誰かほかの人が書いてくれれば、あなたは1度も書かなくてもよい。あなたは、すでにあるものを使えばよいのだ。

 しかし、コンパイラや実行環境だけで汎用のアルゴリズムを現実のプログラムにそのままマッチさせることはできない。現実のプログラムに汎用のアルゴリズムを適用するためには、その間に不足している歯車を用意する必要がある。

 次の図は、ジェネリックを使ったプログラムを模式化した図である。

ジェネリックを使ったプログラムの模式図
現実のプログラムに汎用のアルゴリズムを適用するためには、その間に不足している歯車を用意する必要がある。

 ジェネリックでは上記の図の歯車の部分を記述するが、この図を見た時点で、出来上がるプログラムに対して一般的にいわれる問題は見えている。

「見通しが悪い」

 確かにジェネリックでは、各種汎用アルゴリズムへの接続部分(=歯車の部分)しかコードを記述しないので、汎用アルゴリズムの部分がブラックボックスとなってその仕組みの全体像が見えにくくなる。さらに、例えば歯車の1つがソートならば並べ替え条件に相当するが、各歯車は実際に(並べ替え条件のように)非常に細かい内容である場合が多い。細かいものが散在するコードは確かに見通しが悪いだろう。なお、そのような細かい部品に適切な名前を付けるセンスも必要だ。適切な命名により見通しの悪さが軽減されるからである。

 「見通しが悪い」と指摘される場合によくある最も駄目な例では、コード内で細かい部品を多く呼ぶと全部の部品を理解しようとしてあちこち見て回り、それに疲れて「見通しが悪い」と指摘することだ(筆者は実際にそう指摘された経験がある)。メソッド名を信じないのがその種の人の特徴であり、どこまでも追いかけて行こうとする。メソッド名を信じて読めば何をしているかは手に取るように分かるのにメソッド名を記号だとしか認識しないのである。この種の人の多くは、経験の長いプログラマや、プログラマを経験したプロジェクト・マネージャ層の人であることが問題だ(オブジェクト指向が入る前の時代のプログラマ層と言うべきか)。

 では、本当に仕組みの全体像を見通す必要はあるのだろうか。もし、システム全体を完全に把握することが必要ならば、RDBMSの利用もやめなければならないし、見えないところでファイル操作や並べ替えをするなど、もってのほかだ。さらに、システムを載せているコンピュータはLSI(=集積回路の一種)の固まりだが、その全体像を見通せる人は少ないだろう。

 技術が進化すれば、すべてを見通す必要性は薄れる。つまり、ジェネリックにより仕組みの細部への見通しが悪くなることは「技術の宿命」だ。その宿命を受け入れて気にせずに前に進もう。そうすることが、新しい技術を積極的に取り入れていかなければ生き残れない「開発者の宿命」である。

 それではさっそく、ジェネリックを使った実際のプログラムを説明していこう。

 

 INDEX
  [特集] C# 2.0新機能徹底解説(前編)
  開発生産性を飛躍的に高めるジェネリック
  1.ジェネリックとは?
    2.ジェネリック・コレクションの利用
    3.汎用アルゴリズムを実装しているList<t>クラスのメンバ
    4.ジェネリックを使ったコレクション・クラスの拡張
  [特集] C# 2.0新機能徹底解説(後編)
  進化したC# 2.0の状態管理、匿名メソッドとイテレータ
    1.匿名メソッドとその正体
    2.イテレータの衝撃!
    3.イテレータを解剖する
    4.確実な後処理
 


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間