ここでは複素数を定義する次のような構造体を使って、ユーザー定義の型変換について見ていきます。

struct Complex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }
}

.NET Framework 4よりSystem.Numerics.Complex構造体が使えるようになっているため、通常このような構造体を独自に定義する必要性はあまりありませんが、ここではユーザー定義の型変換の例として独自の複素数型を取り上げています。 .NET Frameworkで提供されるComplex構造体については複素数型で解説しています。

§1 文字列への変換

§1.1 ToString

文字列への変換をサポートするためには、ToStringをオーバーライドすることが出来ます。

using System;

struct Complex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  // ToStringのオーバーライド
  public override string ToString()
  {
    return string.Format("({0:F4}, {1:F4})", Real, Imaginary);
  }
}

class Sample {
  static void Main()
  {
    Complex c1 = new Complex(-1.414, 1.414);
    Complex c2 = new Complex( 3.000, 4.000);

    Console.WriteLine("{0} {1}", c1.ToString(), c1);
    Console.WriteLine("{0} {1}", c2.ToString(), c2);
  }
}
実行結果
(-1.4140, 1.4140) (-1.4140, 1.4140)
(3.0000, 4.0000) (3.0000, 4.0000)

もちろん、次の例ように妥当な理由がある場合は文字列化専用のメソッドを用意しToStringメソッドをオーバーライドしないという方法を採ることもできます。 ただ、すべてのユーザー定義型はObjectを継承していて、ランタイムにより文字列化される際にObject.ToStringメソッドが呼び出されることがあるため、このメソッドをオーバーライドすることで文字列化される際の動作を定義しておいた方がよいでしょう。

using System;

struct Complex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  // 文字列化のためのメソッド
  public string ConvertToString()
  {
    return string.Format("({0:F4}, {1:F4})", Real, Imaginary);
  }
}

class Sample {
  static void Main()
  {
    Complex c1 = new Complex(-1.414, 1.414);
    Complex c2 = new Complex( 3.000, 4.000);

    Console.WriteLine("{0} {1}", c1.ConvertToString(), c1);
    Console.WriteLine("{0} {1}", c2.ConvertToString(), c2);
  }
}
実行結果
(-1.4140, 1.4140) Complex
(3.0000, 4.0000) Complex

§1.2 IFormattable

IFormattableインターフェイスを実装することで、書式を指定した文字列化をサポートすることができます。 また、書式だけでなくカルチャを考慮した文字列化のサポートもIFormattableインターフェイスを用いて実装出来ます。 IFormattableについての詳細は書式の定義と実装で解説しています。

using System;

struct Complex : IFormattable {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  // Object.ToStringのオーバーライド
  public override string ToString()
  {
    return ToString(null, null);
  }

  // IFormattable.ToStringの実装
  public string ToString(string format, IFormatProvider formatProvider)
  {
    if (format == "P") {
      // 極座標表示に書式化
      double r = Math.Sqrt(Real * Real + Imaginary * Imaginary);
      double t = Math.Atan2(Imaginary, Real) / Math.PI;

      return string.Format(formatProvider, "{0:F4}∠{1:F4}π", r, t);
    }
    else {
      // ガウス平面座標表示に書式化
      return string.Format(formatProvider, "({0:F4}, {1:F4})", Real, Imaginary);
    }
  }
}

class Sample {
  static void Main()
  {
    Complex c1 = new Complex(-1.414, 1.414);
    Complex c2 = new Complex( 3.000, 4.000);

    Console.WriteLine("{0} {0:P}", c1);
    Console.WriteLine("{0} {0:P}", c2);
  }
}
実行結果
(-1.4140, 1.4140) 1.9997∠0.7500π
(3.0000, 4.0000) 5.0000∠0.2952π


§2 基本型への変換

§2.1 ToXXX

他の基本型への変換を定義するもっとも簡単な方法は、文字列化の際と同様、目的の型への変換用のメソッドを用意することです。 次の例では、Complex型をDouble型の値として返すメソッドToDoubleを用意し、複素数の絶対値を返すようにしています。

using System;

struct Complex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  public double ToDouble()
  {
    return Math.Sqrt(Real * Real + Imaginary * Imaginary);
  }
}

class Sample {
  static void Main()
  {
    Complex c1 = new Complex(-1.414, 1.414);
    Complex c2 = new Complex( 3.000, 4.000);

    double d1 = c1.ToDouble();
    double d2 = c2.ToDouble();

    Console.WriteLine(d1);
    Console.WriteLine(d2);
  }
}
実行結果
1.99969797719556
5

ただ、このようなメソッドを用意しても、Convertクラスを使った変換などが出来るようになるわけではありません。 次のコードを実行すると、例外InvalidCastExceptionがスローされます。

using System;

struct Complex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  public double ToDouble()
  {
    return Math.Sqrt(Real * Real + Imaginary * Imaginary);
  }
}

class Sample {
  static void Main()
  {
    Complex c1 = new Complex(-1.414, 1.414);
    Complex c2 = new Complex( 3.000, 4.000);

    double d1 = Convert.ToDouble(c1);
    double d2 = Convert.ToDouble(c2);

    Console.WriteLine(d1);
    Console.WriteLine(d2);
  }
}
実行結果
ハンドルされていない例外: System.InvalidCastException: 型 'Complex' のオブジェクトを型 'System.IConvertible' にキャストできません。
   場所 System.Convert.ToDouble(Object value)
   場所 Sample.Main()

§2.2 IConvertible

Convertクラスを用いた型変換をサポートするには、IConvertibleインターフェイスを実装しなければなりません。 このインターフェイスには、他の基本型への変換を行うためのメソッドが用意されています。 これらのメソッドでは、変換を定義できる場合は変換結果を返し、変換を定義できない場合はInvalidCastExceptionをスローするようにします。

以下はIConvertibleを実装する例です。 この例のComplex型では、IConvertibleを次のように実装しています。

  • Double型への変換では、Complex型の表す複素数の絶対値を返す
  • その他の実数型・整数型への変換では、Complex型の表す複素数の絶対値を目的の型に変換して返す
  • Boolean型の変換では、実部・虚部ともに0の場合はfalse、そうでなければtrueを返す
  • String型への変換では、ガウス平面座標表示に書式化した文字列を返す
  • System.Drawing.PointF型への変換をサポートする
  • それ以外の型への変換はサポートしない(InvalidCastExceptionをスローする)
  • IConvertible.ToString以外は直接呼び出せないように明示的な実装にする
using System;
using System.Drawing;

struct Complex : IConvertible {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  TypeCode IConvertible.GetTypeCode()
  {
    // ユーザ定義型であるTypeCode.Objectを返す
    return TypeCode.Object;
  }

  double IConvertible.ToDouble(IFormatProvider provider) {
    return Math.Sqrt(Real * Real + Imaginary * Imaginary);
  }

  float IConvertible.ToSingle(IFormatProvider provider)
  {
    return Convert.ToSingle((this as IConvertible).ToDouble(provider));
  }

  decimal IConvertible.ToDecimal(IFormatProvider provider)
  {
    return Convert.ToDecimal((this as IConvertible).ToDouble(provider));
  }

  byte IConvertible.ToByte(IFormatProvider provider)
  {
    return Convert.ToByte((this as IConvertible).ToDouble(provider));
  }

  short IConvertible.ToInt16(IFormatProvider provider)
  {
    return Convert.ToInt16((this as IConvertible).ToDouble(provider));
  }

  int IConvertible.ToInt32(IFormatProvider provider)
  {
    return Convert.ToInt32((this as IConvertible).ToDouble(provider));
  }

  long IConvertible.ToInt64(IFormatProvider provider)
  {
    return Convert.ToInt64((this as IConvertible).ToDouble(provider));
  }

  sbyte IConvertible.ToSByte(IFormatProvider provider)
  {
    return Convert.ToSByte((this as IConvertible).ToDouble(provider));
  }

  ushort IConvertible.ToUInt16(IFormatProvider provider)
  {
    return Convert.ToUInt16((this as IConvertible).ToDouble(provider));
  }

  uint IConvertible.ToUInt32(IFormatProvider provider)
  {
    return Convert.ToUInt32((this as IConvertible).ToDouble(provider));
  }

  ulong IConvertible.ToUInt64(IFormatProvider provider)
  {
    return Convert.ToUInt64((this as IConvertible).ToDouble(provider));
  }

  bool IConvertible.ToBoolean(IFormatProvider provider)
  {
    return (Real != 0.0 || Imaginary != 0.0);
  }

  public override string ToString()
  {
    // IConvertible.ToString(IFormatProvider)の結果を返す
    return ToString(null);
  }

  public string ToString(IFormatProvider provider)
  {
    return string.Format(provider, "({0:F4}, {1:F4})", Real, Imaginary);
  }

  object IConvertible.ToType(Type conversionType, IFormatProvider provider)
  {
    if (conversionType == typeof(PointF)) {
      return new PointF(Convert.ToSingle(Real), Convert.ToSingle(Imaginary));
    }
    else {
      throw new InvalidCastException();
    }
  }

  char IConvertible.ToChar(IFormatProvider provider)
  {
    throw new InvalidCastException();
  }

  DateTime IConvertible.ToDateTime(IFormatProvider provider)
  {
    throw new InvalidCastException();
  }
}

class Sample {
  static void Main()
  {
    Complex c1 = new Complex(-1.414, 1.414);
    Complex c2 = new Complex( 3.000, 4.000);

    Console.WriteLine("ToString");
    Console.WriteLine("{0} {1} {2}", c1, c1.ToString(), Convert.ToString(c1));
    Console.WriteLine("{0} {1} {2}", c2, c2.ToString(), Convert.ToString(c2));

    double d1 = Convert.ToDouble(c1);
    double d2 = Convert.ToDouble(c2);

    Console.WriteLine("ToDouble");
    Console.WriteLine("{0} => {1}", c1, d1);
    Console.WriteLine("{0} => {1}", c2, d2);

    bool b1 = Convert.ToBoolean(c1);
    bool b2 = Convert.ToBoolean(c2);

    Console.WriteLine("ToBoolean");
    Console.WriteLine("{0} => {1}", c1, b1);
    Console.WriteLine("{0} => {1}", c2, b2);

    int i1 = Convert.ToInt32(c1);
    int i2 = Convert.ToInt32(c2);

    Console.WriteLine("ToInt32");
    Console.WriteLine("{0} => {1}", c1, i1);
    Console.WriteLine("{0} => {1}", c2, i2);

    float s1 = (float)Convert.ChangeType(c1, TypeCode.Single);
    float s2 = (float)Convert.ChangeType(c2, TypeCode.Single);

    Console.WriteLine("ChangeType TypeCode.Single");
    Console.WriteLine("{0} => {1}", c1, s1);
    Console.WriteLine("{0} => {1}", c2, s2);

    PointF p1 = (PointF)Convert.ChangeType(c1, typeof(PointF));
    PointF p2 = (PointF)Convert.ChangeType(c2, typeof(PointF));

    Console.WriteLine("ChangeType PointF");
    Console.WriteLine("{0} => {1}", c1, p1);
    Console.WriteLine("{0} => {1}", c2, p2);

    Console.WriteLine("ToDateTime");
    Console.WriteLine("{0} => {1}", c1, Convert.ToDateTime(c1));
  }
}
実行結果
ToString
(-1.4140, 1.4140) (-1.4140, 1.4140) (-1.4140, 1.4140)
(3.0000, 4.0000) (3.0000, 4.0000) (3.0000, 4.0000)
ToDouble
(-1.4140, 1.4140) => 1.99969797719556
(3.0000, 4.0000) => 5
ToBoolean
(-1.4140, 1.4140) => True
(3.0000, 4.0000) => True
ToInt32
(-1.4140, 1.4140) => 2
(3.0000, 4.0000) => 5
ChangeType TypeCode.Single
(-1.4140, 1.4140) => 1.999698
(3.0000, 4.0000) => 5
ChangeType PointF
(-1.4140, 1.4140) => {X=-1.414, Y=1.414}
(3.0000, 4.0000) => {X=3, Y=4}
ToDateTime

ハンドルされていない例外: System.InvalidCastException: 指定されたキャストは有効ではありません。
   場所 Complex.System.IConvertible.ToDateTime(IFormatProvider provider)
   場所 System.Convert.ToDateTime(Object value)
   場所 Sample.Main()

§3 暗黙的・明示的な型変換

ここまでではメソッドやインターフェイスを通した型変換を行う例を解説してきましたが、型変換演算子をオーバーロード(多重定義)することで型変換演算子(キャスト演算子、CType)を用いた型変換をサポート出来るようになります。

§3.1 暗黙的な型変換(拡大変換)演算子のオーバーロード

C#ではimplicit operator、VBではWidening Operator CTypeを用いることで暗黙の型変換演算子をオーバーロード出来ます。 この演算子は、拡大変換を定義する場合にオーバーロードします。 変換先の型は必ずしも基本型である必要は無く、変換が定義できるならどのような型への変換も実装出来ます。 また、変換先の型を複数定義することも出来ます。

次の例は、Complex型を暗黙的にDouble型およびString型へと変換できるよう演算子をオーバーロードする例です。

using System;

struct Complex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  public override string ToString()
  {
    return string.Format("({0:F4}, {1:F4})", Real, Imaginary);
  }

  // doubleへの型変換演算子
  public static implicit operator double(Complex c)
  {
    return Math.Sqrt(c.Real * c.Real + c.Imaginary * c.Imaginary);
  }

  // stringへの型変換演算子
  public static implicit operator string(Complex c)
  {
    return c.ToString();
  }
}

class Sample {
  static void Main()
  {
    Complex c1 = new Complex(-1.414, 1.414);
    Complex c2 = new Complex( 3.000, 4.000);

    double d1 = c1;
    double d2 = c2;

    Console.WriteLine("{0} => {1}", c1, d1);
    Console.WriteLine("{0} => {1}", c2, d2);

    string s1 = c1;
    string s2 = c2;

    Console.WriteLine("{0} => {1}", c1, s1);
    Console.WriteLine("{0} => {1}", c2, s2);
  }
}
実行結果
(-1.4140, 1.4140) => 1.99969797719556
(3.0000, 4.0000) => 5
(-1.4140, 1.4140) => (-1.4140, 1.4140)
(3.0000, 4.0000) => (3.0000, 4.0000)

型変換演算子となるメソッドはpublic static/Public Sharedでなければなりません。 また、暗黙の型変換演算子のオーバーロードする際にはいくつかの注意が必要です。

暗黙の型変換演算子がオーバーロードされている場合、異なる型の変数への意図しない代入でも変換が行われてしまうことになります。 上記の例では、Complex型から暗黙的にString型へ変換することが出来るようになっていますが、これは他の多くの基本型とは異なる動作であるため、混乱を招く可能性があります。 このような場合の他、変換によりデータの欠損や桁落ち、オーバーフローが発生する場合は、代わりに明示的な型変換演算子を用いるべきです。 言い換えると、暗黙の変換が行われても予期しない結果となることもなく、変換により例外が発生しないような場合には、暗黙の型変換演算子をオーバーロードしても問題にはならないと言えます。

§3.2 明示的な型変換(縮小変換)演算子のオーバーロード

C#ではexplicit operator、VBではNarrowing Operator CTypeを用いることで明示的な型変換演算子をオーバーロード出来ます。 この演算子は、縮小変換を定義する場合にオーバーロードします。 暗黙的な型変換演算子と同様、変換先の型は必ずしも基本型である必要は無く、変換が定義できるならどのような型への変換も実装出来ます。 また、変換先の型を複数定義することも出来ます。

次の例は、Complex型を明示的にInteger型およびPointF型へと変換できるよう演算子をオーバーロードする例です。

using System;
using System.Drawing;

struct Complex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  public override string ToString()
  {
    return string.Format("({0:F4}, {1:F4})", Real, Imaginary);
  }

  public static explicit operator int(Complex c)
  {
    // 実部・虚部の値が非数もしくは無限大の場合は、OverflowExceptionをスローする
    if (double.IsNaN(c.Real) || double.IsNaN(c.Imaginary) ||
        double.IsInfinity(c.Real) || double.IsInfinity(c.Imaginary))
      throw new OverflowException("NaN or Infinity");

    return Convert.ToInt32(Math.Sqrt(c.Real * c.Real + c.Imaginary * c.Imaginary));
  }

  public static explicit operator PointF(Complex c)
  {
    return new PointF(Convert.ToSingle(c.Real), Convert.ToSingle(c.Imaginary));
  }
}

class Sample {
  static void Main()
  {
    Complex c1 = new Complex(-1.414, 1.414);
    Complex c2 = new Complex( 3.000, 4.000);

    int i1 = (int)c1;
    int i2 = (int)c2;

    Console.WriteLine("{0} => {1}", c1, i1);
    Console.WriteLine("{0} => {1}", c2, i2);

    PointF p1 = (PointF)c1;
    PointF p2 = (PointF)c2;

    Console.WriteLine("{0} => {1}", c1, p1);
    Console.WriteLine("{0} => {1}", c2, p2);

    Complex c3 = new Complex(double.NaN, double.NegativeInfinity);

    Console.WriteLine("{0} => {1}", c3, (PointF)c3);
    Console.WriteLine("{0} => {1}", c3, (int)c3);
  }
}
実行結果
(-1.4140, 1.4140) => 2
(3.0000, 4.0000) => 5
(-1.4140, 1.4140) => {X=-1.414, Y=1.414}
(3.0000, 4.0000) => {X=3, Y=4}
(NaN (非数値), -∞) => {X=NaN (非数値), Y=-∞}

ハンドルされていない例外: System.OverflowException: NaN or Infinity
   場所 Complex.op_Explicit(Complex c)
   場所 Sample.Main()

暗黙的な型変換演算子と同様、明示的な型変換演算子となるメソッドはpublic static/Public Sharedでなければなりません。 また、暗黙的な型変換演算子とは異なり、変換に際してOverflowExceptionInvalidCastExceptionFormatExceptionなどの例外をスローすることが出来ます。

上記の例では、非数もしくは無限大の値を持つ場合、Integer型への変換時にOverflowExceptionをスローするようにしていますが、値が変換できない形式ならFormatException、定義できない変換であればInvalidCastExceptionをスローするように出来ます。

§3.3 相互変換が可能な型でのオーバーロード

二つの型で型変換演算子をオーバーロードすることで、型を相互に変換できるようにすることが出来ます。 以下の例では、直交形式と極形式の二つの形式で複素数を表現する型を用意し、相互に変換できるよう演算子をオーバーロードしています。

using System;

struct CartesianComplex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public CartesianComplex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  public override string ToString()
  {
    return string.Format("({0:F4}, {1:F4})", Real, Imaginary);
  }

  // PolarComplexへの型変換
  public static explicit operator PolarComplex(CartesianComplex c)
  {
    return new PolarComplex(Math.Sqrt(c.Real * c.Real + c.Imaginary * c.Imaginary),
                            Math.Atan2(c.Imaginary, c.Real));
  }
}

struct PolarComplex {
  public double Magnitude; // 絶対値
  public double Phase; // 偏角

  public PolarComplex(double magnitude, double phase)
    : this()
  {
    this.Magnitude = magnitude;
    this.Phase = phase;
  }

  public override string ToString()
  {
    return string.Format("{0:F4}∠{1:F4}π", Magnitude, Phase / Math.PI);
  }

  // CartesianComplexへの型変換
  public static explicit operator CartesianComplex(PolarComplex c)
  {
    return new CartesianComplex(c.Magnitude * Math.Cos(c.Phase),
                                c.Magnitude * Math.Sin(c.Phase));
  }
}

class Sample {
  static void Main()
  {
    CartesianComplex c1 = new CartesianComplex(-1.414, 1.414);
    CartesianComplex c2 = new CartesianComplex( 3.000, 4.000);

    PolarComplex p1 = (PolarComplex)c1;
    PolarComplex p2 = (PolarComplex)c2;

    Console.WriteLine("{0} => {1}", c1, p1);
    Console.WriteLine("{0} => {1}", c2, p2);

    c1 = (CartesianComplex)p1;
    c2 = (CartesianComplex)p2;

    Console.WriteLine("{0} => {1}", p1, c1);
    Console.WriteLine("{0} => {1}", p2, c2);
  }
}
実行結果
(-1.4140, 1.4140) => 1.9997∠0.7500π
(3.0000, 4.0000) => 5.0000∠0.2952π
1.9997∠0.7500π => (-1.4140, 1.4140)
5.0000∠0.2952π => (3.0000, 4.0000)

この例では、CartesianComplexからPolarComplexへの変換はCartesianComplexで、PolarComplexからCartesianComplexへの変換はPolarComplexで定義していますが、型変換演算子をオーバーロードする場合は必ずしも変換元の型に実装しなければならないというわけではなく、変換前・変換先の型ならどちらでも定義することが出来ます。 次の例では、先の例での型変換演算子の両方をPolarComplexでオーバーロードしています。

using System;

struct CartesianComplex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public CartesianComplex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  public override string ToString()
  {
    return string.Format("({0:F4}, {1:F4})", Real, Imaginary);
  }
}

struct PolarComplex {
  public double Magnitude; // 絶対値
  public double Phase; // 偏角

  public PolarComplex(double magnitude, double phase)
    : this()
  {
    this.Magnitude = magnitude;
    this.Phase = phase;
  }

  public override string ToString()
  {
    return string.Format("{0:F4}∠{1:F4}π", Magnitude, Phase / Math.PI);
  }

  // CartesianComplexからPolarComplexへの型変換
  public static explicit operator PolarComplex(CartesianComplex c)
  {
    return new PolarComplex(Math.Sqrt(c.Real * c.Real + c.Imaginary * c.Imaginary),
                            Math.Atan2(c.Imaginary, c.Real));
  }

  // PolarComplexからCartesianComplexへの型変換
  public static explicit operator CartesianComplex(PolarComplex c)
  {
    return new CartesianComplex(c.Magnitude * Math.Cos(c.Phase),
                                c.Magnitude * Math.Sin(c.Phase));
  }
}

class Sample {
  static void Main()
  {
    CartesianComplex c1 = new CartesianComplex(-1.414, 1.414);
    CartesianComplex c2 = new CartesianComplex( 3.000, 4.000);

    PolarComplex p1 = (PolarComplex)c1;
    PolarComplex p2 = (PolarComplex)c2;

    Console.WriteLine("{0} => {1}", c1, p1);
    Console.WriteLine("{0} => {1}", c2, p2);

    c1 = (CartesianComplex)p1;
    c2 = (CartesianComplex)p2;

    Console.WriteLine("{0} => {1}", p1, c1);
    Console.WriteLine("{0} => {1}", p2, c2);
  }
}
実行結果
(-1.4140, 1.4140) => 1.9997∠0.7500π
(3.0000, 4.0000) => 5.0000∠0.2952π
1.9997∠0.7500π => (-1.4140, 1.4140)
5.0000∠0.2952π => (3.0000, 4.0000)

当然ながら、型変換演算子からは他の型の非パブリックなメンバにはアクセス出来ないので、この例の様な方法が採れる場合は多くはありません。

§4 その他

§4.1 Converter<TInput, TOutput>

Converterジェネリックデリゲートは任意の型変換を行うメソッドを表すデリゲートです。 このデリゲートは、Array.ConvertAllList.ConvertAllなどのメソッドで、特定の型から別の型に変換する場合に使われます。 型パラメータTInputとTOutputで変換前と変換後の型を任意に指定することが出来ます。 このデリゲートと変換を行うメソッドを組み合わせて使うことで、型変換演算子が定義されていない・Convertクラスで変換できない場合や異なる変換ルールを使用したい場合に、独自に変換処理を定義して型の変換を行えるようになります。

次の例では、Converterデリゲートを使って複素数Complex型の配列をPoint型の配列に変換しています。

using System;
using System.Drawing;

struct Complex {
  public double Real; // 実部
  public double Imaginary; // 虚部

  public Complex(double real, double imaginary)
    : this()
  {
    this.Real = real;
    this.Imaginary = imaginary;
  }

  public override string ToString()
  {
    return string.Format("({0:F4}, {1:F4})", Real, Imaginary);
  }
}

class Sample {
  // ComplexをPointに変換するメソッド
  static Point ConvertToPoint(Complex c)
  {
    return new Point(Convert.ToInt32(c.Real), Convert.ToInt32(c.Imaginary));
  }

  static void Main()
  {
    Complex[] cxs = new[] {
      new Complex(-1.414, 1.414),
      new Complex( 3.000, 4.000),
      new Complex( 0.000, 0.000),
    };

    foreach (Complex e in cxs) {
      Console.WriteLine(e);
    }

    Point[] pts = Array.ConvertAll(cxs, ConvertToPoint);

    foreach (Point e in pts) {
      Console.WriteLine(e);
    }
  }
}
実行結果
(-1.4140, 1.4140)
(3.0000, 4.0000)
(0.0000, 0.0000)
{X=-1,Y=1}
{X=3,Y=4}
{X=0,Y=0}