List 7-10には1箇所弱点がある。それは、14行目で、Class1とは縁もゆかりもない無関係なクラスにキャストしてしまっても、コンパイラはエラーを通知してくれないということだ。List 7-11のソース・コードで実際にそれを確かめてみよう。
1: using System;
2:
3: namespace Sample011
4: {
5: public class Class2
6: {
7: public String hello;
8: }
9: class Class1
10: {
11: public String hello;
12: [STAThread]
13: static void Main(string[] args)
14: {
15: Class1 c = new Class1();
16: c.hello = "Hello!";
17: object o = (object)c;
18: Class2 c2 = (Class2)o;
19: Console.WriteLine("{0},{1}",c.hello,c2.hello);
20: }
21: }
22: }
ここでは、まったく無関係だが、たまたま同じ「hello」という変数を持つClass2というクラスを宣言し、18行目でobjectクラスからもとのクラスにキャストし直す際に、この無関係なClass2を指定してみた。実際にビルドしてみると分かるが、明らかに間違っているにもかかわらず、コンパイラは何もエラーを告げない。
しかし、これが動作するはずもなく、実際に実行すると、Fig.7-11のようなエラー・メッセージで強制終了させられてしまう。
'System.InvalidCastException' のハンドルされていない例外が sample011.exe で発生しました。
追加情報 : 指定されたキャストは有効ではありません。
このように、コンパイル時に判断できないバグを作り込んでしまう可能性があるので、インスタンスのキャストは必要なとき以外は使わないほうがよい。一方、どんな型のインスタンスでも扱える便利なクラスを開発する際は、このリスクを考えに入れても、インスタンスをobject型にキャストして使う価値があるといえる。
7-10 スーパークラスへのキャスト
無関係なクラスへのキャストはエラーになるしかないが、スーパークラスへのキャストは可能である。例えば、Class2を継承してClass1が作られているとき、Class1のインスタンスをClass2にキャストするのは正しい使い方の範囲内である。
実際にList 7-11をList 7-12のように小改造して、試してみよう。
1: using System;
2:
3: namespace Sample012
4: {
5: public class Class2
6: {
7: public String hello;
8: }
9: class Class1 : Class2
10: {
11: [STAThread]
12: static void Main(string[] args)
13: {
14: Class1 c = new Class1();
15: c.hello = "Hello!";
16: object o = (object)c;
17: Class2 c2 = (Class2)o;
18: Console.WriteLine("{0},{1}",c.hello,c2.hello);
19: }
20: }
21: }
List 7-12のポイントは、Class1のインスタンスとして作ったはずのインスタンスをobjectにキャストしてから、あらためてClass2にキャストしていることである。しかし、9行目を見ると分かるとおり、Class2はClass1のスーパークラスであるため、このキャストは合法である。実際に、helloという変数は、Class2の中だけに存在するものであり、15行目で代入したものと、18行目で参照している変数helloはすべて同一である。
これを実行した結果は、Fig.7-12のようになる。
ボックス化とキャストも関係がある。ボックス化/ボックス化解除というのは、データ変換の一種だからだ。List 7-13に簡単なボックス化/ボックス化解除のサンプル・ソースを示す。
1: using System;
2:
3: namespace Sample013
4: {
5: class Class1
6: {
7: [STAThread]
8: static void Main(string[] args)
9: {
10: int i = 123;
11: object o = i;
12: int j = (int)o;
13: Console.WriteLine("{0},{1},{2}",i,o,j);
14: }
15: }
16: }
これを実行した結果は、Fig.7-13のようになる。
List 7-13では、11行目で、あっさりと整数をobject型に代入しているが、ここでボックス化が発生する。ボックス化で情報が欠落することはまったくないので、キャストしなくても問題なく通る。これに対して、12行目でボックス化した値を整数型変数に代入する時点で、ボックス化解除が行われる。この場合には、「(int)」とキャストされていることから分かるとおり、キャストが必要とされている。ボックス化ではキャストはなくてもよいが、ボックス化解除ではキャストを必須とする、と覚えておくとよいだろう。
Copyright© Digital Advantage Corp. All Rights Reserved.