C#ではキャストを使う機会が多いです。
というのも、ArrayListなどのコンテナがobjectを入れるようになってますし、イベントハンドラなんかもobjectが渡されるようになってることが多いからです。
ただ、C#にはC風のキャストの他にas演算子があります。
これらについてまとめてみます。
キャストはCの構文と変わりません。
|
ただ、構文自体はCのキャストと同じですが、機能は違います。
Cのキャストは「何でもあり」でおかしなキャストをしないようにするのはコードを書く人の責任です。
C#のキャストは、単純型(int、doubleなど)に対してはCのキャストと同じような感じで、その他の型(値型、参照型すべて)に対しては実行時型チェック付きのキャストになります。
なので、Cのキャスト + C++のdynamic_castって感じです。
まず、Cのキャストと同じようなところから。
int、doubleなどといった単純型の型変換については仕様が決められています。
「C#言語仕様 6. 変換」に書かれています。
「6.1 暗黙の変換」は、自動的にやってくれるキャストです。
たとえば、intからdoubleへは暗黙の変換をしてくれるので、わざわざキャストする必要ありません。
|
逆にdoubleからintへは明示的な変換が必要とされているので、自分でキャストする必要があります。
|
キャストしないと「error CS0029: 型 'double' を型 'int' に暗黙的に変換できません。」というコンパイルエラーになります。
まぁ、「桁数の小さいほうから大きいほうへは代入可能、大きいほうから小さいほうへは桁落ちするから明示的なキャストが必要(桁落ちして構わないという意思表示が必要)」ということですね。
この仕様は、Cとほとんど同じです。
続いて、その他の型に対するキャスト。
継承関係があれば子供から親へは暗黙の変換をしてくれます。
|
インターフェースも親だと思ってもらって構いません。 だから、インターフェースへも暗黙の変換をしてくれます。
|
また、すべての型はobjectから派生してるので、objectには何でも放り込めることになります。
(C++では親を持たないクラスを作れますが、CLSではできません。
親を指定しない場合は、自動的にobjectから継承します。
また、intとInt32はどう違う?でも書いたようにintなどの単純型も実体はSystem.Int32などで、親をたどればobjectに行き着きます。)
親から子孫へは明示的な変換が必要です。
|
インターフェースからそれを実装しているクラスへや、インターフェースから別のインターフェースへの変換も明示的な変換が必要です。
|
派生関係のない、赤の他人にはキャストできません。
|
こういうキャストをするとSystem.InvalidCastException例外が発生します。
この辺がCのキャストではなくって、C++のdynamic_castだってところです。
as演算子の構文はこんな感じです。
|
キャストとは逆に「変換先の型」が"as"の右側にきます。
また、as演算子は、型を変換するという意味ではキャストと同じです。
ただ、キャストとはいろいろと違うところがあります。
まず、as演算子は参照型にしか使えません。
|
たとえ、intをSystem.Int32と書いてもダメです。値型(struct)には使えません。
また、変換できないときは単にnullが返ります(例外は出ません)。
|
インターフェースからそれを実装しているクラスへや、インターフェースから別のインターフェースへの変換はキャストと同様にできます。
|
ユーザ定義の変換演算子も呼ばれません。
|
(上の例だと、実際にはコンパイルエラーになっちゃいますが)
基本的には、
という感じでいいと思ってます。
もうちょっと具体的に言うと、
|
こんなコードがあったとします。
ここで、aryには「仕様上、TestClassにキャストできるオブジェクト(TestClass自身かTestClassを継承したもの)しか入らない」という前提があるのなら、これでいいと思います。
逆に、aryには「仕様上、何が入るかわからないのでTestClassにキャストできるとは限らない」のであれば、あまりよろしくないと思います。
もちろん、後者の場合でも例外をきちんと処理すれば、プログラム上は問題ありません。
けど、例外はやっぱり例外として扱いたいじゃないですか。
例外処理は比較的重い部類の処理っていう理由もありますが、それより、例外は「本来なら発生しないはずのもの」だと思うんです。
前者なら「ここでInvalidCastExceptionが出るってことは、他のどっかがバグってる」ってことなので、まさに例外ですが、後者だと「当然、例外は出るけどかまやしねぇ」って感じだと思うんです。
「例外をif文代わりに使っちゃやだ」と言ってもいいですね。
だから、Windows.Formsのイベントハンドラなんかでも、
|
で構わないと思うんです。
じゃあ、後者の場合はどうするかって言うと、
|
とas演算子を使うのが自然だと思うんです。
as演算子とよく似たis演算子(as演算子の「キャストできるかチェックするだけ版」です)を使えば、
|
と書けますが、これはちょっともったいないです。
これだと、is演算子のときに「キャストできるかチェック」して、キャストのときにもういっぺん「キャストできるかチェック」することになっちゃうからです。
基本的にはこれでいいと思ってるんですが、ちょっと悩ましいのがユーザ定義の変換演算子です。
変換演算子は、as演算子だと呼ばれないのでキャストにしなきゃいけません。
おまけに、変換演算子の存在理由は「知らないうちにうまいことつじつまをあわせてくれる」ってとこだと思うんです。
クラスを使う立場のときに「えっと、このクラスは変換演算子を呼ばなきゃダメだから、as演算子を使っちゃダメで...」
なんてこと考えなきゃいけないんだったら、最初から変換演算子なんて使わずにToHoge()みたいな変換用のメソッドを用意したほうがよっぽどましです。
けど、それだと「常にキャストを使う」ってことになっちゃいますし。
このへんは、まだ考えがまとまってません(まぁ、よほどのことがない限り変換演算子なんて使うなってことになるとは思いますが)。