連載:[完全版]究極のC#プログラミングChapter2 ジェネリック川俣 晶2009/08/17 |
|
本記事は、(株)技術評論社が発行する書籍『[完全版]究極のC#プログラミング ― 新スタイルによる実践的コーディング』から、許可を得て転載しています。 手元でまとめて読みたい方は、ぜひ書店などにてお買い求めください。 【注意】本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。 |
2.1 ジェネリックとは何か?
前章では、C# 3.0がC# 1.xとはまったく異質な言語であることを示したが、本章で解説する「ジェネリック」(Generic)にはそのような大胆さはない。あくまで、C# 1.xプログラミングを行っているときに日常的に感じる不満を解消するための新機能である。しかし、それでいて、C# 3.0らしいプログラミングを縁の下から支える基礎技術であるので、ないがしろにはできない。まずは、これから手を付けていくことにしよう。ジェネリックにはすでに習熟しており、C# 3.0らしいプログラミングを知りたいという読者は、本章を読み飛ばしていただいてもかまわない。
さて、それではC# 1.xプログラミングの不満とは何だろうか?
具体的にはいろいろあると思うが、おそらく次の点については、ほとんどのC# 1.xプログラマーが不満と感じているだろう。
- コレクションの要素にアクセスする際、キャストが要求される
具体的にいえば、次のリスト2.1のようなことである。
| |
リスト2.1 C# 1.xでコレクションを使用した例 |
このプログラムでは、可変サイズの1次元リストコレクションを実現するArrayListクラスに文字列を入れてから取り出している。ここで、何事もなく文字列をコレクションに入れることができるのに、取り出す際には、
|
というキャストが必要となる。そして、このキャストはいくつもの面倒をもたらす。
まず、書かなければならない文字が増えて面倒である。本質的に必要のない文字がソースコード上に増えれば、それだけ可読性も悪くなり、メインテナンス性も落ちる。だが、これはやってくる災厄の中では序の口にすぎない。
キャストを用いると、それだけ実行速度が低下する。キャストは重い処理なのである。ちなみに、C#ではキャストよりもas演算子を使うほうが高速なのだが、as演算子は変換できないときにnullという値になるため、そのまま例外を投げてくれるキャストと比較して、いまひとつ扱いにくい。
そして、キャストを用いると、型変換の妥当性のチェックをコンパイル時に行うことができなくなり、それは実行時に遅延される。軽量言語の信奉者の中には、「型は実行時にチェックされるから問題ない」と主張する者もいるが、本質的にこれは何が問題であるかを見失った発言といえる。型のチェックが実行時まで遅延されるということは、チェックが「遅延」されるという問題ではなく、型のチェックが「個別の値」に対して行われるという問題なのである。
より具体的に見てみよう。キャストを用いると、まず次のような状況が発生する。
- テスト実行の際、すべての行が実行されるとは限らない。つまり、実行されなかった行に書かれたキャストの妥当性はチェックされない
これは、テスト支援ツールなどを用いて、カバレッジ(網羅)率100%を目指せば解消できるかもしれない。
しかし、次のような問題は解消できない。
- たまたまテスト実行の際に与えられた値に対してキャストが妥当であることは確認されるが、実行時にありうるすべての値に対してキャストが妥当であるかはチェックされない
このような問題は、小さなプログラムでもバグを生むことがあるが、規模の大きなプログラムでは相当のリスクになる。たとえば、コレクションに思いもよらない型の値が入っていて、うまくいくはずのキャストが失敗する……といった状況は、仕様変更の多いプロジェクトや意思疎通が不十分な開発プロジェクトでは珍しくもないだろう。
このような問題は、できるだけ多くの型チェックをコンパイル時に行うことで回避できるが、C# 1.xでは限界がある。
もちろん、それが不可能と思うならば、大きな不満にはならないだろう。しかし、歴代のマイクロソフト社製開発ツールを使ってきた技術者であれば、C#よりも古くから使われてきたC++ではそれが可能であることを知っているはずである。C++の「template」と呼ばれる機能は、まさに汎用のコレクションクラスからキャストを追放する手段として使用できたのだ。つまり、不可能ではないことが明らかであるがゆえに、この問題はC# 1.xでの大きな不満となっていたのだと思う。
そして、C# 3.0はその問題を解消する新機能を追加した。ただし、それはtemplate機能ではなく、ジェネリックという似て非なる機能である*1(本章の「 【C#olumn】 C++のtemplate機能との相違」参照)。
この機能を使うとリスト2.1からキャストを追放できることを確認するためのサンプルコードを、リスト2.2に示す。
| |
リスト2.2 C# 3.0でコレクションを使用した例 |
このとおり、変数listを宣言する際に、
|
のように、stringという型を明示することにより(記述方法の詳細は後述)、ソースコード上からキャストは消滅した。
ちなみに、型を指定する関係上、型の名前が長くなっている。そして、これを「List<string> list = new List<string>();」のように1行に2回も書くのは面倒であり、また、可読性も落とすという意見もありうるだろう。それについては、第12章「12.4 なぜvarを使うのか?」を参照いただきたい。実際には「var list = new List<string>();」と書けば十分である。
*1 C++のtemplateは一般用語としてのジェネリックの一種であるともなされる場合もあるが、C#のジェネリックとC++のtemplateは異なる構造で実現された異なる機能である。 |
INDEX | ||
[完全版]究極のC#プログラミング | ||
Chapter2 ジェネリック | ||
1.2.1 ジェネリックとは何か? | ||
2.2.2 新しいコレクションの紹介 | ||
3.2.3 新しいコレクションクラス―LinkedListクラス | ||
4.2.4 新しいコレクションクラス―SortedDictionaryクラス | ||
5.2.5 ジェネリックコレクションの使い方 | ||
6.2.6 ジェネリックメソッドと型推論 | ||
7.2.7 HashtableクラスとDictionaryクラスの非互換性 | ||
8.2.8 ジェネリックなクラスを自作する | ||
9.2.9 制約の付いたジェネリックなクラス/C++のtemplate機能との相違/練習問題 | ||
「[完全版]究極のC#プログラミング」 |
- 第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用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|