- 第2回 ラムダ式と型推論
最初の節はラムダ式そのものよりも、Attribute(Reflection)の効率的な使用法だと思えます。ここではラムダ式、delegateを使うことにより、Reflectionの参照を最小限に抑えている点の方が役に立っています。まあ、ラムダ式の使い方の一種ではあるのでしょうね。
次の節の前半はシステム規定のdelegateの型。ActionとPredicateは良く使いそうです(というか、このレベルのdelegateを自前で定義しないよいうに注意しないといけませんね)。Funcシリーズはちょっと微妙。頭に記載されているように、汎用過ぎて正当性のチェックとかが無意味になってしまう可能性が出てきます。このレベルになると自前で宣言した方が可視性が上がるように思えますね。後半はラムダ式での型推論の話。確かに型推論で記述量が減るのは認めますが、こういうシステムの入り組んだ処理に頼って記述を減らすのはどうかと思いますね。良く言われる例ですが、演算子の優先順位に頼らず、無駄に括弧を付けた方が良い、というのを思い出します。まあ、単純/明快な推論でカバーできるならともかく、ここの最後の例なんかはあまり嬉しく無いでしょう。ちなみに個人的にはエレガントとというよりは悪趣味の世界に入り込んでいると思いますね。
前節の後ろから次節の前半で、やっと本質的なdelegateとlambdaの違いが出てきました。本質的にはdelegateは匿名(無名)関数、lambdaは式ツリー(Expression Tree)なのですね。ただ、式ツリーをdelegateの式(関数)としても使用可能だということのようです。式ツリーはコンパイル時に生成されるものなので、コードの中で動的に変更可能な式を作成する手段が提供されることになります。通常のコードではこのような機能が使いません。しかし次回予告にあるようにLINQがこれを使って実現されているようで、たしかに検索の条件を実行コードに落とすようなケースでは必要になりそうです。 - 第3回 varによる変数宣言とコレクション初期化子
ちなみに私、個人的には「過剰な否定」派であること明記しておきます。LLでなんども意図しない型変換で泣いておりますので。でまずは、コンパイル時に型が確定するのでひと安心です。これも型推論に基づく省略記法に過ぎないのでしょうか?たしかにオブジェクトをnewして変数に格納するときの冗長性は腹がたちますので、これが簡略化されるのは悪いことではありません(このケースでは型推論と言っても明確そのものです)。ただ型推論が難しくなるようなケース(次節の配列問題とか)ではやはり避けたいところです。
最後の節はコレクション初期化子の話題で、C#ではコレクションに対する初期化子の記述が可能になったというものです。前に触った時だと、perlでは不可、pythonでは可能だったように記憶しています。でコレクションの初期化を使いたいばかりにpythonで書いたりしましたが。便利になった、と思ったら単なるシンタックスシュガーとのこと。ちょっとがっかりですが、ソースは見た目、ということでOKとしましょう。Dictionaryにも初期化設定が可能になっているのは有り難いですね。 - 第4回 自動実装と自動定義
自動実装プロパティって便利なのでしょうか?よく使うパターンでは、内部的には自由なアクセスを、外部からは特定のメソッドによるアクセスのみを提供しますが、内部からでもプロパティ経由でしかアクセスできないという点で引いてしまいます。確かに一部の(オブジェクト指向をどう誤解したんだよという)アクセッサ至上主義の人達には愛用されそうですが。単純なデータ保持のためのクラスと言ったものの場合には便利になるのかもしれません。そういえば、この手のデータ保持のためのクラスみたいなものも無いわけではありませんからね。
と、次の節ではまさにデータを保持するためだけの匿名型が出てきました。初期化のみ可能な読み出し専用のプロパティを持ったクラスです。型チェック云々を気にしなければDictionaryででも実装できそうなデータオブジェクトです。ただ、C#では、初期化子の識別名と初期化値に基づく型が推定されるようで、コンパイル時に型チェックの対象となる点でしっかりした型(オブジェクト)になっています。で、何に使うかといえば、LINQのselectの結果を保持するため、といいきってもいいかと。同様に、データ項目が動的に変化するような処理結果を保持するデータ型として使えるでしょう(古い言語ではKey/Valueペアの集合で表現していたようなものです)。
最後の節は、オブジェクト初期化子の話。匿名型はオブジェクト初期化子からの型推論によって自動的に(名無しの)型(オブジェクト)を生成していたわけで、初期化子自体は任意のオブジェクトの初期化に使えます。ここではオブジェクトの初期化の順序/制限について解説しています。宣言→コンストラクタ→初期化子という順番はまあ期待通りなので何も問題はありません。問題になるのは、初期化子ではprivate/readonlyフィールドへの設定ができないという点でしょう。確かに仕組みでいえば当然なのですが、あるクラスで固定的なデータを用意するような場合にはオブジェクト初期化子による生成ができない点は残念です。概念的にはオブジェクトの初期化と考えるよりも構造体の初期化に近いイメージで捉えた方が良さそうです。 - 第5回 拡張メソッド
普通にクラスで定義するメソッドでも、インスタンスメソッドとして定義したもの「obj.method()」と、自オブジェクトをパラメタとして渡すクラスメソッド「method(obj)」とを用意することがあります。内部データにアクセスしないのであればクラスメソッドは外部で定義しても構いません。拡張メソッドは、このような外部で定義された(オブジェクトインスタンスをパラメタとする)メソッドを、クラスメソッドと同じ形式で呼び出すことができるようにした、ある種のシンタックスシュガーのようです。まあ、既存クラスに対するある種の拡張システムと考えればいいのでしょう。
2010-03-21
C#勉強中 C# 3.0入門 2-5
間が空きましたが C# 3.0入門の続きをば
2010-03-16
C#勉強中 C# 3.0入門 1
2010-03-12
C#勉強中 C# 2.0入門 6-最終回
C# 2.0入門続き
- 第6回 部分クラスと静的クラス
最初の節はdelegateによる変数のキャプチャの話。delegateによるキャプチャが生成されると、delegateが生きている限り変数(オブジェクト)も生き続けるので、Garbage Collectionされないという注意。いわれてみればもっともな話ですが、見逃しやすい点です。Javaでもそうでしたが、オブジェクトのメモリがどのようなタイミングで回収されるかをイメージするのはそれなりに重要かと思います。ただ長時間動作し続けるアプリケーションでなければ気にすることも無いでしょう。
部分クラスは便利ですね。クラスのコードが巨大化して保守し難くなることがありますし、すべてのコードが密に結合しているわけでもありません。場合によっては、部分クラスによってファイル分割するのも便利でしょう。
静的クラスも、なぜ他の言語に無いのか不思議になる機能ですね。Singleton Patternなんぞと銘打つぐらいなら最初からこういうクラスを用意しておけばいいのに。まあ、ある種の後知恵なのかもしれません。アクセッサの拡張(アクセシビリティ制御)は、最近VSを使い始めた私からすると、おや、当初は無かったんだ、といった機能です。こうやってみると、初期のC#(VBもでしょうね)では、いまや普通に使うような機能が結構漏れていたのですね。 - 第7回 名前空間のエイリアス修飾子と外部アセンブリ
List3でエラーになる理由が最初わかりませんでした。名前空間はグローバルな名前空間から識別名毎にネストした構成になっていて、ある識別名への参照は、ローカルな名前空間から始まって、(個別の識別名が)最初に一致したところから、再度下位の空間を探索していくという仕様になっているのですね。で、List3の場合、A.B.C.SayHelloは、最初はSample.Main空間で探索して見つからないので、次に、その上位のSample名前空間で探索されます。すると、Sample空間に名前A(Sample.A)があるので、そこで、B.C.SayHelloを探すことになります。すると、B.Cまでは見つかりますが、そこにはSayHelloは無いのでエラーになるわけですね。じっくり考えるとごもっとも、ですが、これは間違え易そうです。ここではglobal::で参照先を明確化していますが、汎用的な解決方法としては次に節で説明されているextern aliasで指定するべきなのでしょう。
InternalsVisibleToも使い勝手のよさそうな属性です。コード上、特定のクラスでしか使わないクラスは結構ありますので、無駄にパブリックにしない方法が提供されているのは有り難いですね。 - 最終回 小さな改善とコンパイラの新機能、そして3.0への展望
まあ、ここは読み飛ばしですね。確かにさっさとC#3.0の勉強に入らないと。
ただ、ざっと見た感想をいうと、ひとこと「なにか胡散臭い」、でした。せっかくオブジェクト指向言語使っていて、こういうテストしていのかな?オブジェクト指向言語の場合はオブジェクトレベルでの動作確認が簡単にできるのに、異なったレベルのテストで、わざわざ面倒にしているとしか思えません。
2010-03-11
C#勉強中 言語仕様の進化
delegateとclosureの違いを調べている時に偶然見つけたページですが
「C# 1.1からC# 3.0まで~言語仕様の進化」
(ほぼ)同じ処理が言語の進化に応じて、どのように表現が変わっていくかで示したものです。旧版にどのような問題点があったか、それが新版でどのように解消されるかが、実際のコードベースで記載されています。これは読み易いし、イメージが掴みやすいですね。
「C# 1.1からC# 3.0まで~言語仕様の進化」
(ほぼ)同じ処理が言語の進化に応じて、どのように表現が変わっていくかで示したものです。旧版にどのような問題点があったか、それが新版でどのように解消されるかが、実際のコードベースで記載されています。これは読み易いし、イメージが掴みやすいですね。
2010-03-10
C#勉強中 C# 2.0 入門 5
C# 2.0 入門続き
- 第5回 匿名メソッドとデリゲート
delegateそのものは判りやすいし、いかにも使い勝手が良さそうに思えます。難しくなるのはdelegateが定義された場所での上位スコープの変数への参照が許される点です(というか、これってクロージャ?)。このあたりの用語は結構言語毎に微妙に違うので判りにくいものがあります。要はdelegateにはコードそのものと、そのコードが参照するスコープ内の変数のセットが含まれるわけですが、変数の「何が」含まれるのかが問題になるわけですね。ここのサンプルコードによると、変数値ではなく、(動的に生成された)変数そのものが含まれる(束縛される)ようです。結果、for ループ内で制御変数(そのもの)を参照するdelegateを定義すると、すべてが同じ変数を含んでしまうため、最終的な制御変数値への参照になってしまうということでしょう。
C#勉強中 ラムダ式
@ITの記事からラムダ式の話
delegate自体が他の言語で言うところのラムダ式だと思っていたのですが、delegateとは別にラムダ式があるのですね。実際にはdelegateに対するシンタックスシュガーのようです。使い分けが問題かな。Cでの三項演算子とif文の関係みたいなものかと考えています。ちょっとしたコードならラムダ式表現の方が見易くなるのは確かです。でもCで三項演算子嫌う人がいるように、ラムダ式を嫌う人がいそうです。
delegate自体が他の言語で言うところのラムダ式だと思っていたのですが、delegateとは別にラムダ式があるのですね。実際にはdelegateに対するシンタックスシュガーのようです。使い分けが問題かな。Cでの三項演算子とif文の関係みたいなものかと考えています。ちょっとしたコードならラムダ式表現の方が見易くなるのは確かです。でもCで三項演算子嫌う人がいるように、ラムダ式を嫌う人がいそうです。
2010-03-09
C#勉強中 C# 2.0 入門 2-4
C# 2.0 入門続き
- 第2回 ジェネリック
最近になって.NET系を使い始めた私にとっては、最初期にはGenericが無かったことの方が驚きです。新しいオブジェクト指向言語でなら当然の機能だと思っていました。VBではいやというほどGeneric型を使っていたので、ここはC#での記法を学ぶだけで過ごしましょう。 - 第3回 新しい繰り返しのスタイル - yield return文とForEachメソッド
反復子?ここではRubyの例がでていますが、昔にちょっと触ったPythonにもありました。そういえば、Excelのセルアクセスでも使いましたね。使う方はごく簡単ですが、作成する方は面倒ですね。そういえば反復子のコード、何かのサンプルで見たことがありましたが、なにやら解りにくかった記憶があります。まあやりたい事は判るのですが通常のコードとは全く異なった動作になりますから。ここの説明を読んでやっと内容(というかこのコードがどのような実行パターンに展開されるか)が理解できました。最後の節で実際の展開イメージが記載されいます。これを見れば一目瞭然です。
と、ここまで反復子の話を書いてきて最後にCollectionオブジェクトのForEachメソッドを挙げますか?しかも、yield returnはめったに使わないし、こちらの方が遥かに早いと。これはJavaなんかのMapと同系列の機能ですね。単純にコレクション内のすべてのデータについての処理を行なうなら、こちらの方が適していますね。 - 第4回 Findメソッドとnull許容型
前半はCollectionのFind、 FindAllメソッドの話題で、これは前章のForEachメソッドと同じ系列のものです。ForEachは要素に対してある演算を施していくのに対して、Find、FindAllは特定条件を満たす要素をピックアップしていくものです。これもいまや当たり前の機能です。
で、後半はnull許容型の話題。たしかにSQL連携しているとやたらとnullが出てきて、それらが簡単に処理可能になるのは解ります。言語仕様的にはあればいいかなとは思いますが、システム設計的には私もNULL撲滅委員会にシンパシーを感じる方なので、めったに使わないかと思います(だいたいデフォルト値への代用で済みますからね)。
2010-03-08
C#勉強中 C# 2.0 入門 1
改定版 C#入門読み終わったので引き続いて続編のC# 2.0 入門を読んでいきます。 現時点で使っているのはVS2008 ExpressでC#も3.0ですので、読み進めて最新版を追い掛けないといけません。
- 第1回 総論:C# 2.0らしいプログラミングとは
まあ、総論なので読み飛ばしてもいいのでしょうが。delegateキーワードによる匿名関数の話が主になっています。lambda式みたいのものですよね?変数の参照スコープとかは異なっているのかもしれませんが。今ひとつピンときません。後の詳細説明でちゃんと考えてみることにします。要は「C# 1.xプログラマーよ、心してかかれ!」ということだけですね。
C#勉強中 rainプログラム
週末でしたので、いままで勉強してきた知識でプログラムをひとつ書いてみました。いままで読んできたコードの大半は入出力には.NetのConsoleを使用しています。Consoleの仕様を見てみると、コマンドプロンプトへの単純な入出力だけでなく、文字ベースでの画面制御機能がひと通り含まれています。ということは、unixのcurse(あるいはOS/2でのVIO)相当のことが実現できるはずです。というわけで、Consoleでの画面制御機能を使ったプログラムを書いてみました。→アーカイブ
元ネタはunixのrainというプログラムで、Ascii Artで雨粒の様子を表現するという、何の役にも立たないものです。多分、cursesのサンプル、といったプログラムかと思います。調べたらNetBSDにはまだ含まれいるのですね。NetBSD版では循環バッファを使って雨粒の位置と状況(段々と波紋が広がっていく様子)を保持しています。バッファ自体には雨粒の位置が記録され、循環バッファ上の位置(現在位置からのオフセット)が状況に対応していて、その状況に応じたAAが表示されています。これはコンパクトな実装ですが、雨粒の数は固定的になっていますし、あまり読み易いコードとはいえません。
C#のようなオブジェクト指向言語で実装するなら、雨粒をオブジェクトで表現し、任意数の雨粒をリストでFIFO管理すると簡単に読み易く実現することができます。雨粒はRainDropクラスで表現しています。これは位置と生成されてからの時間経過を保持していて、時間経過に伴って、AAが表示され世代が古くなっていきます。プログラムの本体はRainクラスで表現し、複数の雨粒をRainDropのリストで保持してみました。時間とともに新しい雨粒が生成されてリストに追加され、一定時間を過ぎた(Expired)雨粒はリストから排除されていきます。
なお、rainプログラムのAAはUS-ASCIIを想定しているので日本語のコマンドプロンプトではあまり綺麗に表現されません(バックスラッシュと円記号の問題)。Consoleには表示される文字のエンコードを属性で参照/設定することができます(Console.OutputEncoding)。そこで、Rain生成時にエンコーディングをUS-ASCII(Encoding.ASCII)に設定し、廃棄時に(IDisposable実装)元のエンコーディング戻す機能も入れてみました。しかし、コマンドプロンプトから実行した場合にはエンコーディングは変更されないようです。ですが、start で別セッションとして起動した場合にはASCII表示に切り替えられます。
C#勉強中 改定版 C#入門 21
改定版 C#入門も最後の章となりました。
- 第21章 ポインタを使用できる「安全でないコード」
ポインタというか、メモリ領域への直接アクセスの話題です。速度を気にしなければ使う必要は無いようです。キーワードとして unsafe、fixedがある、ということだけ覚えておけば充分でしょう。ま、チューニング、決して嫌いではないのですが、一般的な話題ではないです。
2010-03-06
C#勉強中 改定版 C#入門 20
引き続き、改定版 C#入門を。
- 第20章 実行時に参照可能な属性
属性については、VSのヘルプの説明が簡潔でいいですね。先にこちらの定義を記載しておいて欲しいところです。VSのヘルプでは、(1) 属性はプログラムに埋め込まれたメタデータで、(2) 実行時にリフレクションによって参照する、と記載されています。属性そのものに付いての説明はこれで充分でしょう。ただ実際にどのように定義して、どのように使うかについてが、この章で解説されているわけです。
なお、C#からのWin32API呼び出しも属性によって対応付けを行なうようで、20-8 Win32APIの呼び出し、20-9 込み入ったAPI呼び出し、で使用方法が解説されています。Win32APIの呼び出しができるということは、C#処理系や .NETの制約を受けることなくOSのすべての機能を使えることを意味しています。実際にはWin32APIだけに限定されずに、任意の外部関数の呼び出しができるようですね。ここは重要なポイントです。
Webサービスの定義もC#属性を使うそうで、20-10 Webサービスで使われる属性、20-11 より高度なWebサービス、で解説されています。が、差し当たっては(Webサービスを作成する予定はないので)、属性で定義するという点だけを記憶して読み飛ばしておきます。
2010-03-05
C#勉強中 改定版 C#入門 18-19
引き続き、改定版 C#入門を。
- 第18章 例外とエラー処理
と、ここはVB.NETやJavaと同じなので軽く。 - 第19章 プリプロセッサ
どういう実装になっているのかな?本来的なプリプロセッサなら、C#の実行分の前のみ、などという制限は有り得ないわけで。実質的にはC#コンパイラに組み込まれているのでしょうか。
2010-03-03
C#勉強中 改定版C#入門 17
改定版C#入門の続き
- 第17章 言語に内蔵されたイベント機能
おもいっきり特殊機能に入ってきました。小さなサンプルで説明しようとしていることで却ってイベント機能を判りにくくしているように思えます(とはいえ大きなサンプルはそれ自体難解になってしまいますが)。この章の最後のページ「ウィンドウにおけるイベントの使われ方」が一番簡単に読めました。
イベント機能を多用するウィンドウシステムのレベルまで落としてみると判り易くなりそうです。ウィンドウシステムでコントロールが外部にある事象の発生を通知する場合には、そのコントロール固有のウィンドウメッセージを送出し、メッセージ処理ルーチンがそのメッセージに対応した処理を行なうようになっています。この場合メッセージを送出する側(パブリッシャ)と受け取る側(サブスクライバ)とは、メッセージの意味とパラメタについて合意が取れている必要があります。しかし、単純なメッセージシステムではこのような合意を強制することができません(このため障害が出やすくなります)。
C#のイベント機能はこのようなメッセージの送信側と受信側での合意を強制する(コンパイル時、実行に保証する)機能と考えることができると思います。メッセージの送出(イベント発行)はイベントが定義されたクラス(継承を含む)からのみ可能になっています。従って関係の無いオブジェクトがイベントを発行することはありません。メッセージ自体は、イベント機能の中に隠蔽されていて、外部からは見ることができません。ただ、発生するイベントのハンドラは、任意のクラスにおいて定義でき、抽象化されたイベントに登録(ここでdelegateを使用)できるようになっています。たいていのプログラミングではイベントのパブリッシャはシステム提供され、アプリケーション側ではそれに対するハンドラを作成、登録するだけで済みます。
アプリケーションサイドで知る必要があるものは、抽象化されたイベント(eventキーワードで指示)と、そのイベントに対するハンドラを定義するためのdelegate(に適合するメソッドの型)だけになります。ただこの章では、イベントのパブリッシャも想定しているようで、前半のテキストにはパブリッシャ、サブスクライバの両方が説明されているため、判りにくくなっているかと思います。通常のプログラマ相手であれば、サブスクライバ側だけで充分で、またサブスクライバ側だけだと極端に簡単になります。
2010-03-02
C#勉強中 改訂版 C#入門 8-16
改訂版 C#入門の続き
- 第8章 式と演算子
まあ、このあたりは基本事項。 - 第9章 ステートメント
これも基本事項で他の言語と同様。 - 第10章 名前空間とusing
名前空間を持つ言語でなら同様。別名機能は他では見ない点かも。単純化されている分、Javaのパッケージより使い勝手がよさそう。 - 第11章 コンストラクタとデストラクタ
usingステートメントに注目(名前空間の方はusingディレクティブ)。アンマネージリソースを管理するようなオブジェクトの場合には明示的な廃棄処理が必要になります。しかしC#では(Javaでも)オブジェクトのデストラクタの実行タイミングは制御できません。で、そのようなオブジェクトにはIDisposableインターフェイスを実装し、usingステートメントで廃棄処理を明示的に呼び出すようにするようです。 - 第12章 インデクサとプロパティ
いってしまえば両方ともシンタックスシュガーに過ぎないのですが、うまく使えばソースの可読性を向上させます。特にインデクサは便利な機能です。 - 第13章 処理を委譲するデリゲート
段々とC#に特徴的な機能に入ってきました。ちなみに、delegateをカタカナ表記する場合にデリゲート、デレゲートの両方が使われることがあります。改定版では無い方のC#入門ではデレゲート表記になっていたりしてます。
デリゲートは、C#(および一部のオブジェクト指向言語に)特徴的な機能で、特定の関数エントリに対して実際の処理関数を動的に切り替えることを可能にするものです。関数ポインタ云々といった記述がありますが、確かにデリゲートの方が本質的な機能であって、デリゲート機能を持たない言語ではこのような機能を(しょうがなく)関数ポインタで実装しているといえるでしょう。XサーバのGCによる描画関数の切り替え(描画対象とラスタオペレーションによって描画関数を切り替える)なんかはデリゲートがあればもっとすっきりした形で実装できたのではないかと思います。 - 第14章 インターフェイスの活用
まあ、Javaと同じといっていいでしょう。 - 第15章 C#の配列機能
このあたりも標準的。List 15-4 のコードと実行結果との対応が解り難かったけど、よくよく見ると文字列の長さになっているのですね。生成直後での例外発生を確認するためとはいえ、少々悩みました。 - 第16章 列挙型の活用
ここも全くもって標準的。
2010-03-01
C#勉強中 改訂版 C#入門 3-7
改訂版 C#入門の続き
- 第3章 クラスとインスタンス
C#だと、クラス変数へのインスタンスを介したアクセスが禁止されるのですね。 - 第4章 継承とインターフェイス
基本的にはJavaなんかと同じで。 - 第5章 C#のデータ型
Primitive/Object問題きれいに回避してますね。 - 第6章 変数と値型・参照型
refはともかくoutは使う機会はなさそうです。 - 第7章 キャストとデータ変換
ユーザ定義の型変換、便利そうですが、モジュラリティの観点からはすっきりしません。
継承されたクラスと親のクラスとの間とかなら理解できますが、相互に独立しているクラス間でこのような変換を定義するということは、あるクラスが他のクラスの実装に依存するような形になるわけで、クラスの独立性を損ねるような気がします。むしろ、相互に変換可能なクラス群を含めた変換クラスといったものを定義するほうがよさそうに思えます。
今日はこんなところで。
C#の勉強、始めました
登録:
投稿 (Atom)