連載

C#入門 最終回

第22回 落ち穂拾い

(株)ピーデー
川俣 晶
2002/03/01


ユーザー定義のデータ型変換

 複数のデータ型の間では、データ型の変換が行われる。例えば、整数を実数型の変数に代入することができるが、これは整数から実数へのデータ型変換の機能が動作することで実現するものである。整数と実数の間の変換なら、あらためて自分で変換を定義したいと思うことは少ないかもしれない。だが、自作のクラスから別のクラスへの変換機能を使用する必要に迫られることがあるかもしれない。このような場合には、C#のユーザー定義のデータ型変換機能を使うと、すっきりと扱うことができる。以下はそれを用いた例である。

 1: using System;
 2:
 3: namespace ConsoleApplication2
 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:     static void Main(string[] args)
22:     {
23:       CentiMeter cm = new CentiMeter(123);
24:       MilliMeter mm = cm;
25:       Console.WriteLine( "{0}cm,{1}mm", cm.length, mm.length );
26:     }
27:   }
28: }
ユーザー定義のデータ型変換機能を定義したサンプル・プログラム2
14行目でセンチ・メートルからミリ・メートルへの暗黙的(implicit)な変換演算子を定義している。

 これを実行すると以下のようになる。

サンプル・プログラム2の実行結果
CentiMeter型のインスタンスをMilliMeter型の変数へ代入すると、暗黙的に値が変換される。

 このソース・コードで注目すべきは、14〜17行目で定義されている暗黙的な変換演算子である。これが存在することにより、24行目のように、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 ConsoleApplication3
 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:     static void Main(string[] args)
22:     {
23:       CentiMeter cm = new CentiMeter(123);
24:       //MilliMeter mm = cm;   // 型 'ConsoleApplication3.CentiMeter' を型 'ConsoleApplication3.MilliMeter' に暗黙的に変換できません。
25:       MilliMeter mm = (MilliMeter)cm;
26:       Console.WriteLine( "{0}cm,{1}mm", cm.length, mm.length );
27:     }
28:   }
29: }
キャストした場合のみ有効な変換演算子を定義したサンプル・プログラム3
データ型変換機能を定義している部分で、implicitキーワードをexplicitキーワードに変更している。

 これを実行すると以下のようになる。

サンプル・プログラム3の実行結果
キャストせずに変換を実行するような代入はコンパイル・エラーとなる。

 ソース・コードの変更点は2つある。1つは、14行目のimplicitキーワードがexplicitキーワードに変更されたことである。これにより、この変換演算子は、明示的に変換が指定された場合にのみ動作するようになる。その結果、24行目のコードはエラーになり、変換を行うためには、25行目のようにキャストを挿入する必要が生じる。

 さて、変換演算子は、変換されるデータ型のどちら側で定義されても機能する。上記の2つの例は、CentiMeterクラス内に変換演算子を置いたが、これをそのままMilliMeterクラスに移動させてもまったく同じように動作する。以下はそれを示す例である。

 1: using System;
 2:
 3: namespace ConsoleApplication4
 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:     static void Main(string[] args)
22:     {
23:       CentiMeter cm = new CentiMeter(123);
24:       MilliMeter mm = (MilliMeter)cm;
25:       Console.WriteLine( "{0}cm,{1}mm", cm.length, mm.length );
26:     }
27:   }
28: }
変換演算子の定義場所を移動したサンプル・プログラム4
変換演算子の定義場所をCentiMeterクラスからMilliMeterクラスに移動している。変換演算子の内容はまったく変更していない。

 これを実行すると以下のようになる。

サンプル・プログラム4の実行結果
変換演算子の定義場所を変えても動作は変わらない。

 このソース・コードでは、変換演算子の内容は変わっていないことに注目していただきたい。記述された場所が異なっているだけである。そして、動作は同じであることを確認していただきたい。


 INDEX
  第22回 落ち穂拾い
    1.コンポーネントという言葉の意味
  2.ユーザー定義のデータ型変換
    3.演算子を定義する
    4.constとreadonly
    5.シールされたクラス
 
「C#入門」


Insider.NET フォーラム 新着記事
  • 第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用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間