Column - ユーザー定義のデータ型変換 -
複数のデータ型の間ではデータ型の変換が行われる。例えば、整数を実数型の変数に代入することができるが、これは整数から実数へのデータ型変換の機能が動作することで実現するものである。整数と実数の間の変換なら、期待される動作の種類も少なく、あらためて自分で変換を定義したいと思うことは少ないかもしれない。だが、自作のクラスから別のクラスへの変換機能を使用する必要に迫られることがあるかもしれない。その場合、C#のユーザー定義のデータ型変換機能を使うとすっきりと扱うことができる。以下はそれを用いた例である。
1: using System;
2:
3: namespace Sample014
4: {
5: class MilliMeter
6: {
7: public int length = 0;
8: public MilliMeter( int n ) { length = n; }
9: }
10: class CentiMeter
11: {
12: public int length = 0;
13: public CentiMeter( int n ) { length = n; }
14: public static implicit operator MilliMeter(CentiMeter cm)
15: {
16: return new MilliMeter(cm.length*10);
17: }
18: }
19: class Class1
20: {
21: [STAThread]
22: static void Main(string[] args)
23: {
24: CentiMeter cm = new CentiMeter(123);
25: MilliMeter mm = cm;
26: Console.WriteLine( "{0}cm,{1}mm", cm.length, mm.length );
27: }
28: }
29: }
これを実行すると以下のようになる。
このソースで注目すべきは、14〜17行目で定義されている暗黙的な変換演算子である。これが存在することにより、25行目のように、CentiMeter型のインスタンスをいきなりMilliMeter型の変数に代入するという荒技が可能になるのである。暗黙的な変換演算子は、14行目のように書くのが約束である。キーワードoperatorの次に変換する片方のデータ型名を書き、引数の型として変換するもう一方のデータ型名を記述する。この2つのデータ型名の一方は、その変換演算子を記述するデータ型名(クラス名など)と一致している必要がある。14行目の例なら、CentiMeterクラスの定義の中にあるので、14行目の引数の型はCentiMeter型になっている。そして、14行目はCentiMeter型からMilliMeter型への変換を行うことを宣言している。そして、16行目は具体的な変換内容の実装である。新しいMilliMeter型のインスタンスを作成し、その内容は変換元インスタンスが持つ値よりも10倍大きいとしている。これで、24行目を実行したときに、cmの内容は「123」であるのに、mmに含まれる値は「1230」という値になるのである。
暗黙的に自動的に変換が行われると危険だと思う場合は、キャストした場合のみ変換されるような変換演算子も定義可能だ。以下がその例である。
1: using System;
2:
3: namespace Sample015
4: {
5: class MilliMeter
6: {
7: public int length = 0;
8: public MilliMeter( int n ) { length = n; }
9: }
10: class CentiMeter
11: {
12: public int length = 0;
13: public CentiMeter( int n ) { length = n; }
14: public static explicit operator MilliMeter(CentiMeter cm)
15: {
16: return new MilliMeter(cm.length*10);
17: }
18: }
19: class Class1
20: {
21: [STAThread]
22: static void Main(string[] args)
23: {
24: CentiMeter cm = new CentiMeter(123);
25: // MilliMeter mm = cm; // 型 'Sample015.CentiMeter' を型 'Sample015.MilliMeter' に暗黙的に変換できません。
26: MilliMeter mm = (MilliMeter)cm;
27: Console.WriteLine( "{0}cm,{1}mm", cm.length, mm.length );
28: }
29: }
30: }
これを実行すると以下のようになる。
ソース・コードの変更点は2つある。1つは14行目のimplicitキーワードがexplicitキーワードに変更されたことである。これにより、この変換演算子は、明示的に変換が指定された場合のみ動作するようになる。その結果、25行目のコードはエラーになり、変換を行うためには、26行目のようにキャストを入れる必要が生じる。
変換演算子は、変換されるデータ型のどちら側で定義されても機能する。上記の2つの例では、MilliMeterクラス内に変換演算子を置いたが、これをそのままMilliMeterクラスに移動させてもまったく同じように動作する。以下はそれを示す例である。
1: using System;
2:
3: namespace Sample016
4: {
5: class MilliMeter
6: {
7: public int length = 0;
8: public MilliMeter( int n ) { length = n; }
9: public static explicit operator MilliMeter(CentiMeter cm)
10: {
11: return new MilliMeter(cm.length*10);
12: }
13: }
14: class CentiMeter
15: {
16: public int length = 0;
17: public CentiMeter( int n ) { length = n; }
18: }
19: class Class1
20: {
21: [STAThread]
22: static void Main(string[] args)
23: {
24: CentiMeter cm = new CentiMeter(123);
25: MilliMeter mm = (MilliMeter)cm;
26: Console.WriteLine( "{0}cm,{1}mm", cm.length, mm.length );
27: }
28: }
29: }
これを実行すると以下のようになる。
このソースでは、変換演算子の内容は変わっていないことに注目していただきたい。記述された場所が異なっているだけである。併せて、動作は同じであることを確認していただきたい。
『新プログラミング環境 C#がわかる+使える』
本記事は、(株)技術評論社が発行する書籍『新プログラミング環境 C#がわかる+使える』から許可を得て一部分を転載したものです。
【本連載と書籍の関係について 】
この書籍は、本フォーラムで連載した「C#入門」を大幅に加筆修正し、発行されたものです。連載時はベータ版のVS.NETをベースとしていましたが、書籍ではVS.NET製品版を使ってプログラムの検証などが実施されています。技術評論社、および著者である川俣晶氏のご好意により、書籍の内容を本フォーラムの連載記事として掲載させていただけることになりました。
→技術評論社の解説ページ
ご注文はこちらから
Copyright© Digital Advantage Corp. All Rights Reserved.