連載:[完全版]究極のC#プログラミングSpecial Appendix 1
川俣 晶 |
|
ここではざっくばらんに、現役C#プログラマーとして、筆者がどのようにC# 3.0プログラミングを行っているのか、さまざまな角度から告白してみよう。
どういうジャンルのプログラムを書いてる?
主に次のような分野である。
- Webやメール関係のプログラム(比較的大きいものを含む)
- 各種ユーティリティ
- 技術解説のサンプルソース
- 同人ソフト(ゲーム)
プログラミング言語は、いまのところC#が最も多く、ほかにはJavaScriptやAction Scriptを少々。
ちなみに、同人ソフトは仕事で採用するにはリスクのある技術を使ってみるのに適したジャンル。たとえ、完成できなくともそれで誰かが決定的に困ったりはしない。技術的な冒険のための題材である。
ほかにどのような言語を経験してる?
主なものは、次の言語である。
- アセンブラ(6800、8080、Z-80、x86など)
- BASIC
- Pascal
- C
- C++
- Java
ほかにFORTHインタープリタやLISPインタープリタのコア部分の実装を行ったり、トイ言語のコンパイラ/インタープリタの開発、Z-80用のアセンブラ、逆アセンブラ、Z-80 → 8086のソースコードコンバータなども開発している。1990年代からは応用寄りのプログラムを多く書くようになったので、システム/言語系の開発は減ったが、なくなったわけではない。最近では2008年にCOMET IIエミュレータ+CASL IIアセンブラも実装している。つまり、言語を実装する側に片足を突っ込んでいて、小指の先ぐらいは言語の設計側に突っ込んでいる。
C#開発の実績はどう?
現在1人で書いている開発中のC# 3.0プログラム(未完成)のC#ソースファイルの行数をカウントしたところ、46,222行あった。この行数は「たった4万6千行」と思うかもしれないが、「最近特にコンパクトな書き方を意識的に行っている」、「C# 3.0の各種機能を活用してより短くコードを書いている」という点を加味すると、従来型であたりまえに書けば2〜3倍程度の行数に膨れ上がることが考えられる。また、オブジェクト指向プログラミングの決まり事を杓子定規(しゃくしじょうぎ)に守って作成したとすると、5倍ぐらいに膨れ上がる可能性も考えられる。
他の言語と比較してC#は違う?
明らかに違う。
ともかく、コードを書く行為に没頭でき、ノイズがあまり入らない。
書けなくなって手が止まることも少ない。手が止まるのは、たいていの場合技術的な問題ではなく、書くべき内容が決まらない場合である。
ノイズって何?
いろいろある。
自分の中の雑念であったり、外部から要求される「あるべき規範」であったりする。
たとえば、「理想的にはXXはXXとして表現されるべき」と自分で思ったり、誰かが必死にそのように主張していると、どうしてもコードを書いていて、それがちらついてしまう。
これは、特に過去のオブジェクト指向開発ではよく感じられた。たとえば、「フィールドにはアクセサを付けろ」であるとか「フィールドにオブジェクトを持たせないで継承させよ」(多重継承時代の話)であるとか、さまざまな「あるべきコードの姿」を要求する声があった。
しかし、C#に関してはそのような声はあまり聞こえない。たとえば、LINQやラムダ式がサポートされても、「それを使わないことは間違いだから、悔い改めよ」と訴える声は聞こえてこない。
C#を称える意見をあまり見ないのはなぜだろう?
特定のプログラミング言語を称える声が世の中に溢れかえるのは、逆説的にそのプログラミング言語ではプログラムが書きにくいからではないか? つまり、書きやすければ誰もがコードを書くことに没頭して「称える言葉」に費やす時間は減ってしまうからだ。
ちなみに、筆者がこのような原稿を書く理由はそれが「依頼された仕事だから」であって、無償奉仕であればまず書かない。それだけの時間があれば、C#でコードを書くほうが楽しい。そして、言葉で説明するよりも、実際に動くプログラムを提示するほうがはるかに大きな説得力を発揮する。
C#プログラマーは仲間が少ない?
そんなことはない。
ネットを見ているとC#プログラマーは少数派でJava信者からバカにされているように見えるかもしれないが、C#プログラマーは決して少なくはないし、実際にC#で開発されているプロジェクトも珍しくはない。
開発環境はどうする?
C#そのものはマイクロソフトだけの技術ではなく、ISO/IECの国際規格であり、オープンソースのMono projectによる実装などもある。しかし、筆者のお薦めは、Windows上でVisual Studioを使用して開発する方法である。オープンソースは言葉の上ではすばらしい成果や効能を並べることも多いが、実際に使う場合にはドキュメントが不足したり、面倒な設定手順が必要であったり、維持管理に多大な手間を要したりすることが珍しくなく、なかなかコーディングに没頭できない。どうせ自分で読むことのないソースコードが公開されていることよりも、安定的にすぐ使えるツールが提供されているメリットのほうが価値は大きいだろう。
ちなみに、Visual Studioは、Professional以上のEditionを選択するとベターだろう。2008以降であればテスト機能が内蔵されていて、テスト駆動開発もできる。Visual Studioの「with MSDN Subscription」を選択できればモアベターである。Subscriptionであれば新しいVisual Studioや新しいWindowsがリリースされてもすぐに使用できる。特に古いOSから新しいOSまでが即座に入手でき、開発に使用できる点はポイントが高い。開発しているプログラムのユーザー層が幅広い場合、それらのユーザーが使うすべてのOSを揃えるのは容易なことではないからだ。ちなみに、MSDNより提供されるVirtual PCなどの仮想マシン作成ツールを活用することも価値がある。すべてのバージョンのWindowsを実行できるテスト環境も、仮想マシンを活用すれば実現は難しくない。
Visual SourceSafeなどのソースコードの世代管理のソフトもあるとモアベターである。過去の特定の時点のソースコードにいつでも巻き戻せることは、トラブル調査などに強い力を発揮する。
最後に1つだけ付け加えるなら、環境の細部に気に入らない点があっても文句をいわないで手を動かすほうがよい。致命的な問題は放置できないが、細かい問題は「慣れ」で解決できてしまうことも多いからだ。
一方で、Visual Studioには膨大なカスタマイズ設定項目がある。マウスクリック数回で望むとおりの動作に変更できることもあるので、手を抜かないで確認してみることも価値がある。ちなみに、筆者はソースウィンドウの右端で行を折り返し、横スクロールなしですべて見られる設定に変更して愛用している。そのような設定があることに長い間気づかなかったのは痛恨である。同じ轍は踏まないようにしよう。
ほかのプログラミング言語に注目するとすれば?
すでに述べたとおり、その言語を称える声が過剰に多いプログラミング言語は、実際にはコードが書きにくい可能性があるので、あまり気にしないほうがよい。
基本的には、「プログラマー人口が多い人気言語」でありながら「あまり語られる対象にならない」言語が本当にコードを書ける言語である可能性が高い。
筆者の印象では、C言語、PHP、JavaScriptなどがこの条件に該当する。
C言語は低レベルのプログラミングについて知るために学ぶ価値があるかもしれない。少なくとも、C#のunsafeコンテキストをフル活用しようと思うなら、学んでおいて損はない。
PHPはオーソドックスすぎて、刺激を受けられないかもしれない。
JavaScriptのコードは玉石混淆であるが、本当に優れたJavaScriptのコードは読むと良い刺激になるかもしれない。少なくとも、筆者がC# 2.0〜3.0に開眼できたのは、JavaScriptの優れたソースを読んだおかげである。また、Webアプリケーションを作成する際、クライアントサイドで使用されるのもJavaScriptである可能性が高く、学んでおいて損はない。
僕らのゴールはどこにある?
プログラミングとは、本質的には変化し続けるユーザーニーズと環境に対応し続ける行為である。
したがって、正解はないし、自らも動き続ける必要がある。
このことを筆者は、「カオス(混沌)の縁で踊り続ける」という。
これが唯一の正しい状態である。
もし、すべての問題を解決できる万能の答えを見つけ、踊ることをやめたとすれば、それは「秩序への堕落」である。その万能の答えは、実際には存在しないまやかしである。
逆に、主体性を失って状況に踊らされるだけになったら、それは「混沌への堕落」である。この状態にも問題解決能力はない。
したがって、我々にゴールはない。ゴールがあるかのように見えるとしたら、それは袋小路でしかない。
喧嘩を売られたときは?
C#でコードを書いているというと、他のプログラミング言語の信奉者から文句をいわれることもあるだろう。口喧嘩(くちげんか)を売られたらどうすればよいのだろうか?
まずそのような喧嘩は意味がないので買わないことである。論争で相手を負かすよりも、その時間で実際に動くコードを書くほうが価値がある。
それにもかかわらず論争をせざるをえなくなった場合は、次の点に注意を払うこと。
- 日本語ではなくソースコードを根拠に語ること。日本語では、「ありえない結論」を正しいと主張することができ、その場のムードによってはそれが正しいと承認されてしまうことがある。しかし、ソースコードはコンパイル&実行させれば嘘がばれる
- 根拠とするソースコードは、サンプルソースレベルの小さなものよりも、ある程度の規模がある実用ソフトのほうがよい。わざわざ論争をふっかけたがるタイプは、実際にはそのような規模の大きなコードを書けないことが多いので対応できなくなる(ソースを書けないから日本語で勝負しようとする)
- 「正しい結論」を自分から述べてはいけない。揚げ足を取られて「正しい結論」が正しくないことにされてしまう可能性が高い。単に論争に勝つだけなら、相手に「正しい結論」をいわせるほうがよい。たいていの場合、根拠を問い詰めていくと論理は破綻する
オブジェクト指向プログラミングの本は参考になる?
本書冒頭の「はじめに」でも触れたとおり、オブジェクト指向プログラミングの本のかなりの割合は、実際には「銀の弾丸」と「金のハンマー」に冒されており、あまり役に立たない。
しかし、ケント・ベックの著書や、『リファクタリング:プログラミングの体質改善テクニック』(ピアソン・エデュケーション刊)など、有益な書籍もあるので、すべて否定してはいけない。
ただし、これらの書籍はJavaを前提としていることが多く、Javaの言語仕様や制約と直結する問題を解決する記述も多く含まれる。たとえば、デザインパターンのシングルトンは、C#では静的なクラスを使えば特別な工夫を行うことなく実現できてしまうので、あえてパターンとして活用する意味がない。このような例も多いので、内容は取捨選択が必要である。
「オブジェクト脳」は鍛えたほうがよい?
筆者は「オブジェクト脳」という言葉をよく知らないが、もし物事をすべて無意識的に「オブジェクト」を通して解釈する頭脳のことをいうのであれば、それは典型的な「金のハンマー」になる。そのような脳を持った者が目にするものは、すべてオブジェクトによって実現すべきものに見えるだろうが、それは錯覚である。
もし、脳を鍛えるなら、引き出しの多い人間を目指そう。どのみち、唯一絶対に正しい正解などこの世界にもないのだから、武器は多いほうが安心である。特に、提供される武器のバリエーションが多いC#は、こうした考え方と相性も良い。
コーディングで注意することはある?
C#の言語仕様は、短いコードを実現する方向に進化している。
たとえば、ラムダ式には、すでにサポート済みの匿名メソッドとほぼ等価の内容を記述できる機能を含みつつ、「より短く書ける」というアドバンテージを提供する。varによる変数宣言や匿名の型などもソースコードを短くする効能を提供する。
そして、基本的に短いコードは良いコードである。
もちろん、インデントの空白を削ったり、メソッド名などの文字数を切り詰めることで短くしても意味はない。たとえば、以下のような作業は積極的に行う価値がある。
●重複は取り除く
同じコードがいくつもソースコード上に書かれているようなら、それを1つにまとめ、呼び出すようにしよう。高級言語が生まれる前からある定番の対処である。
●よりシンプルで短い構文で同じ機能が実現できるなら、それを使う
たとえば、継承と仮想メソッドで記述された機能を、デリゲートとラムダ式でより短く書き直せるのであれば、そのほうがよい。
●ループ構文を固まりで処理する機能に書き換える
本書の各所で説明したとおり、C# 3.0にはLINQやコレクションに対するメソッドなど、データの集まりに対して作用する機能が多くある。これらを活用することで、ソースコードはより簡潔になる。
●ラムダ式と変数のキャプチャを活用する
あるメソッドから呼び出すメソッドを、そのメソッド内部でラムダ式として記述できるとき、ラムダ式に書き換えるとラムダ式から親メソッドの変数を利用できるようになる。それにより、値を受け渡す引数などを明示的に宣言する必要がなくなり、短くシンプルになる。
そのほかにも、以下のような点に留意してソースコードを書いてみると効能があるかもしれない。
●とりあえず書いてみる
迷ったらとりあえず書いてみる。書けば、それによって気づくことも多くある。悩むよりも手を動かそう。
個別の機能について試す場合は、独立した新しいプロジェクトを作成してそこに書き込んでもよい。作成中のソースコードの場合、確実に元に戻せる状態を確保しつつ、書き換えてみよう。
●日本語名を活用する
これは状況によっては使えない手ではあるが、クラス、メソッド、変数などの名前に日本語を活用するとそれだけでソースコードの書きやすさやわかりやすさが格段に上がる。たとえば、「本店専用会員カードによる季節限定割引率」といった値を格納する変数を宣言する際、これを英語でどう書くか悩むよりも、直接、「int 本店専用会員カードによる季節限定割引率;」と書いてしまうほうが早いし、少なくとも日本語に慣れ親しんだプログラマーであれば意味も読み取りやすい。
●リファクタリングを行う
重複を取り除き、より短いシンプルなコードを維持するように手間を費やすことは価値がある。ただし、つねに理想的なコードを維持しなければならない、と考えてはいけない。それは、リファクタリングを「金のハンマー」扱いすることを意味する。多少の無駄はあってもよいし、必要とされるまでリファクタリングを先に延ばしてもよい。
●自動テストを用意する
クラスやメソッドを対象とする単体(Unit)テストや、ユーザー操作をシミュレーションしてプログラム全体を自動的にテストする機能などは用意するとよい。現在の筆者は、単体テストよりも全体的なテストの自動化を使うことが多いが、いずれにしても「いつでも気軽に実行できる自動テスト」があれば間違ったソースの書き換えを早期に発見でき、安心感が増す。
ただし、すべてのクラス、すべてのメソッド、すべてのケースについて詳細な自動テストを用意すべき、と考えるとやはり手間がかかりすぎて破綻するので注意が必要である。これもまた、テストの「金のハンマー」化である。すべて万遍なくテストを用意するよりも、「ここは絶対に譲れない」という重要な箇所だけていねいに確認するテストを作成するほうが効果は高い。いずれにせよ、時間や予算、つぎ込める手間などの問題から、すべてを完全にカバーするテストを作ろうという試みは、たいていの場合破綻する。
●強制力のない決まり事には馬耳東風
世の中には多くの「XXと書くべき」という“べき”がある。
このうち、言語仕様レベルで提供される機能はできるだけ活用すべきだ、という意見はそのとおりといえる。たとえば、メソッドにはpublicやprivateのようなアクセシビリティを指定できるが、これは必要に応じて正しく指定すべきである。
しかし、そうではない“べき”には必ずしも従う必要はないし、無理があるようなら、むしろ従うべきではない。このような“べき”に反していても、短くシンプルなコードが得られていれば、それはうまく書けたと思ってよいだろう。
念のために補足すれば、“べき”の多くは、論ずるだけ不毛である。価値観は人それぞれなので、よほど具体的な根拠でもない限り、意見の相違が永遠に平行線ということもある。それに対して、コードの短さは客観的に比較できる。たとえば、特定のソースコードを指差して、これが10行か11行かで揉めることはありえない。ツールで行数をカウントさせれば、つねに同じ数値が出るはずである*1。
*1 もちろん、行末の折り返しやファイル末の空行をどう扱うか、といったルールは決めておかなければ、行数がばらついてしまう可能性はありうる。 |
なぜ短いといいの?
理由を挙げよう。
- 書く手間が減る
- 読む手間が減る
- コードの意味を把握するために読み取る必要のある範囲が小さくなる
- スクロールせずにウィンドウ内に表示できる範囲が増えるのでスクロールの手間も減る
- コンパイル時間も減ってイライラが軽減される
- ソースコード修正時に修正を要する箇所が減る可能性が高い
- 書き換える箇所が少なければ、修正ミスが入る確率も減る
- そもそも、書かれた文字が少なければ、単純ミスで紛れ込むバグも減る
ちなみに、プログラミングにおいて勤勉さは“悪”である。同じ結果を得るためにより多くの手順を費やせば、それだけミスが入り込む確率が高くなり、品質が落ちる。したがって、最小限の手間で結果を得ようとする態度は、品質のアップに繋がる。ただし、必要な手順を抜いてしまう行為まで肯定しているわけではない。除去が肯定されるのは、あくまで、なくても結果は変わらない冗長な部分だけである*2。
*2 念のために補足するが、テストなどの品質維持のための手順は除去してよい手順ではない。それは明らかに結果に影響を与える。 |
プログラム設計のコツはある?
ある。
まったく個人的な筆者の感想ではあるが、C# 3.0では“定義を階層化させない”設計が勝利の鍵だという印象がある。
たとえば、クラスと継承を用いて設計を行うと、基本的なクラスから個別のクラスに対して、継承の連鎖が発生する。このとき、基本的なクラスに発生した小さな修正が、そこから派生する多数のクラスに影響を与えることがある。これは修正に手間を要するだけでなく、修正の影響が及ぶ範囲も広いため、書き換えの正しさを確認する手間も多くなる。
これに対して、デリゲートとラムダ式を主体にした設計を行うことで、定義が階層化しないフラットな設計を行うことができる。たとえば、クラスの定義は1つだけで、振る舞いの違いはオブジェクトに与えるラムダ式の違いによって区別する実装であれば、さまざまな振る舞いを示すオブジェクトをたった1つのクラス定義から作り出せる。これが、フラットな設計である。このような設計では、クラスの修正が波及する別クラスの定義は存在せず、単純にそのクラスを利用するコードだけが修正の対象になる(「C# 3.0デザインパターンミニカタログ」の「フラット オブジェクト」や「フラットオブジェクトファクトリ」(p.368〜p.369)参照)。
しかし、依然としてクラスと継承を使ったほうがシンプルに記述できるケースもあることに注意が必要である。たった1つの方法だけで問題を解決しようとする態度は「金のハンマー」的である。
まとめ ― C#を使うべき決定的な理由はある?
ある。
以下、その理由を説明する。
たいていのプログラミング言語は、自らの特徴、個性を持っていて、進化してもそれを堅持する。
このようなプログラミング言語がいくつも集まり、世界の多様性を構成する。ユーザーはさまざまな言語から最適なものを選び取り、利用することができる。
しかし、一度1つのプログラミング言語のプログラマーになると、たった1つのプログラミング言語の世界に住み続けることになる。世界の多様性は彼の目からは見えなくなり、そのプログラミング言語が彼にとっての「金のハンマー」になる。
しかし、C#を使い続けていると、このパターンには落ち込まない。なぜなら、「C#らしいプログラミング」の内容自体が変化しているからだ。C# 1.x時代のC#らしいソースとC# 3.0時代のそれとはまるで違う。さらに、C# 4.0時代になれば動的なコードも書きやすくなるので、さらに変化していくことが予想される。そうして、C#は「金のハンマー」扱いされることに抵抗を示すのである。金のハンマーだと思ってC#を使おうとすると、C#はすでにハンマーではなくノコギリやカンナになっているかもしれないのである。
それによって、C#は「金のハンマー化」という破滅に陥ることを回避しているともいえる。これは、多くのプログラミング言語が熱烈な支持者層を得ることで「金のハンマー化」してしまった状況の対極にあるものである。
これが、C#を使うべき決定的な理由である。
では、C#以外に「金のハンマー化」に抵抗しうるプログラミング言語はないのだろうか?
たとえば、Visual Basicを含むBASIC系言語や、PHP、JavaScript(ActionScript)なども、同様の傾向があると思う。しかし、コードの生産性や言語仕様のシャープさなどを考えると、やはりC#は一歩抜きん出ているように思う。
最後に、次の言葉で締めくくろう。
カオスの縁で踊り続けよう
踊らなくてもよい理想郷はすべてまやかしである
踊り続ければ、つねに緊張感を持って君の判断が試され
判断を通して君が社会の誰かから必要とされていることが証明される
INDEX | ||
[完全版]究極のC#プログラミング | ||
Special Appendix/おわりに | ||
1.Special Appendix 1 現役C#プログラマーが語るC#を使いこなすツボ | ||
2.Special Appendix 2 ステート集約プログラミング | ||
3.おわりに ― C# 1.xから3.0への進化とは? | ||
「[完全版]究極の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用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|