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

§1.1 暗黙の型変換

もっとも基本的な型変換に、代入時の暗黙の型変換があります。 例えばshort→intやint→doubleなど、データの欠損や桁落ち、オーバーフローが発生しない拡大変換(widening conversion)となる場合には、暗黙の型変換を行うことができます。

using System;

class Sample {
  static void Main()
  {
    int i = 42;
    double d;

    d = i; // intからdoubleへの暗黙の型変換が行われる

    Console.WriteLine(i);
    Console.WriteLine(d);
  }
}
実行結果
42
42

§1.2 明示的な型変換 (キャスト)

逆に、int→shortやdouble→intなど、データの欠損や桁落ち、オーバーフローが発生し得る縮小変換(narrowing conversion)なる場合には、暗黙の型変換を行うことはできません。

using System;

class Sample {
  static void Main()
  {
    double d = 3.14;
    int i;

    i = d; // doubleからintへの縮小変換となるので暗黙の型変換はできない
    // error CS0266: 型 'double' を 'int' に暗黙的に変換できません。明示的な変換が存在します。(cast が不足していないかどうかを確認してください)
  }
}

この場合、キャスト構文やCIntなどの型変換関数を使って明示的な型変換を行う必要があります。

using System;

class Sample {
  static void Main()
  {
    double d = 3.14;
    int i;

    i = (int)d; // doubleからintへのキャスト

    Console.WriteLine(d);
    Console.WriteLine(i);
  }
}
実行結果
3.14
3

どのような場合に拡大変換または縮小変換となるかは、以下のドキュメントでまとめられています。

§1.3 オーバーフローのチェック

C#では、checked/uncheckedステートメントを使うことで整数型へのキャストの際にオーバーフローのチェックを行うかどうかを制御することができます。 checkedステートメントを使うと例外OverflowExceptionをスローするようにすることができ、uncheckedステートメントを使うとオーバーフローのチェックを行わないようにすることができます。 この点についてより詳しくは整数型のオーバーフローとチェックで解説しています。

using System;

class Sample {
  static void Main()
  {
    double d = double.MaxValue;
    int i;

    // デフォルトの動作で
    // キャストする
    i = (int)d;

    Console.WriteLine(i);
  }
}
実行結果
-2147483648
using System;

class Sample {
  static void Main()
  {
    double d = double.MaxValue;
    int i;

    // オーバーフローのチェックを
    // 行わずにキャストする
    i = unchecked((int)d);

    Console.WriteLine(i);
  }
}
実行結果
-2147483648
using System;

class Sample {
  static void Main()
  {
    double d = double.MaxValue;
    int i;

    // オーバーフローのチェックを
    // 行った上でキャストする
    i = checked((int)d);

    Console.WriteLine(i);
  }
}
実行結果
ハンドルされていない例外: System.OverflowException:
算術演算の結果オーバーフローが発生しました。
   場所 Sample.Main()


§2 実数型から整数型への変換 (Round, Ceiling, Floor)

float(Single)、doubleおよびdecimalから整数型へ変換する際に、Math.RoundMath.CeilingMath.Floorといった端数処理のメソッドと組み合わせて変換することが出来ます。 これらのメソッドの動作と使い方については数学関数 §.実数の丸め・端数処理 (Truncate, Ceiling, Floor, Round)で詳しく解説しています。

§3 文字列への/からの変換

§3.1 文字列への変換 (ToString)

基本型から文字列への変換にはToStringメソッドを使うことが出来ます。 単純な文字列化の他、書式を指定した変換も出来ます。

using System;

class Sample {
  static void Main()
  {
    int i = 42;
    double d = 3.141592;

    string s1 = i.ToString();
    string s2 = i.ToString("D8");
    string s3 = d.ToString();
    string s4 = d.ToString("F4");

    Console.WriteLine(s1);
    Console.WriteLine(s2);
    Console.WriteLine(s3);
    Console.WriteLine(s4);
  }
}
実行結果
42
00000042
3.141592
3.1416

ToStringメソッドの引数に"D8""F4"などの書式指定文字列を指定することで桁数や数値の表記を指定して文字列化することができます。 ToStringメソッドに指定できる書式については、書式指定子で詳しく解説しています。

§3.2 文字列からの変換 (Parse, TryParse)

文字列から基本型への変換にはInt32.ParseDouble.ParseなどのParseメソッドを使うことが出来ます。

using System;

class Sample {
  static void Main()
  {
    int i1 = int.Parse("42");
    int i2 = int.Parse("000042");
    double d1 = double.Parse("3.14");
    double d2 = double.Parse("1.23E+3");

    Console.WriteLine(i1);
    Console.WriteLine(i2);
    Console.WriteLine(d1);
    Console.WriteLine(d2);
  }
}
実行結果
42
42
3.14
1230

このメソッドでは、変換できない形式の場合はFormatException、変換した値が型の最大値・最小値を超える場合はOverflowExceptionがスローされます。

変換時に例外をスローさせたくない場合は、Int32.TryParseDouble.TryParseなどのTryParseメソッドを使うことが出来ます。 このメソッドは、変換できた場合はtrue、変換できなかった場合はfalseを返し、変換した値は2番目の引数(out/ByRef)に代入されます。

using System;

class Sample {
  static void Main()
  {
    int i;

    if (int.TryParse("42", out i))
      Console.WriteLine(i);
    else
      Console.WriteLine("変換できない値");

    if (int.TryParse("abc", out i))
      Console.WriteLine(i);
    else
      Console.WriteLine("変換できない値");

    if (int.TryParse("01234567890123456789", out i))
      Console.WriteLine(i);
    else
      Console.WriteLine("変換できない値");

    double d;

    if (double.TryParse("3.14", out d))
      Console.WriteLine(d);
    else
      Console.WriteLine("変換できない値");

    if (double.TryParse("1.2345E+6789", out d))
      Console.WriteLine(d);
    else
      Console.WriteLine("変換できない値");
  }
}
実行結果
42
変換できない値
変換できない値
3.14
変換できない値

日付型(DateTime/DateTimeOffset)とParse・TryParseメソッドについては日時・文字列の変換と書式で詳しく解説しています。

§3.3 基数を指定した変換 (Convert.ToXXX)

16進数、8進数などの文字列から整数型に変換するには、Convertクラスの以下のメソッドを使うことが出来ます。 また、基数を指定して整数型の値を文字列に変換することもできます。 いずれのメソッドも、二つめの引数に変換する文字列(または数値)の基数を指定します。

基数を指定した変換を行うことが出来るメソッド
メソッド 動作
文字列から整数型への変換
ToByte(String, Int32) 文字列からByteへの変換
ToInt16(String, Int32) 文字列からInt16への変換
ToInt32(String, Int32) 文字列からInt32への変換
ToInt64(String, Int32) 文字列からInt64への変換
ToSByte(String, Int32) 文字列からSByteへの変換
ToUInt16(String, Int32) 文字列からUInt16への変換
ToUInt32(String, Int32) 文字列からUInt32への変換
ToUInt64(String, Int32) 文字列からUInt64への変換
整数型から文字列への変換
ToString(Byte, Int32) Byteから文字列への変換
ToString(Int16, Int32) Int16から文字列への変換
ToString(Int32, Int32) Int32から文字列への変換
ToString(Int64, Int32) Int64から文字列への変換

基数として指定できる値は、2、8、10、16のいずれかで、これ以外の基数を指定すると例外ArgumentExceptionがスローされます。 また、基数に10以外を指定してマイナス記号で始まる文字列を変換しようとした場合もArgumentExceptionとなります。 さらに、16進数を表す"0x"や"&h"などのプレフィックスを含む文字列を変換しようとした場合は、FormatExceptionがスローされます。

以下は、基数を指定して文字列と整数型の値を変換する例です。

using System;

class Sample {
  static void Main()
  {
    // 数値128を文字列に変換
    Console.WriteLine(Convert.ToString(128, 2)); // 2進数に変換
    Console.WriteLine(Convert.ToString(128, 8)); // 8進数に変換
    Console.WriteLine(Convert.ToString(128, 10)); // 10進数に変換
    Console.WriteLine(Convert.ToString(128, 16)); // 16進数に変換

    Console.WriteLine();

    // 文字列"100"を数値に変換
    Console.WriteLine(Convert.ToInt32("100", 2)); // 2進数として変換
    Console.WriteLine(Convert.ToInt32("100", 8)); // 8進数として変換
    Console.WriteLine(Convert.ToInt32("100", 10)); // 10進数として変換
    Console.WriteLine(Convert.ToInt32("100", 16)); // 16進数として変換
  }
}
実行結果
10000000
200
128
80

4
64
100
256

§4 バイト配列への/からの変換

§4.1 基本型とバイト配列への/からの変換

BitConverter.GetBytesメソッドを用いると、基本型をバイト配列に変換することが出来ます。 逆にBitConverter.ToXXXメソッドを用いると、バイト配列から基本型に変換することが出来ます。 StreamクラスBinaryReaderクラス・BinaryWriterクラスを使ってバイナリデータを扱う場合などにこれらのメソッドが役立ちます。

ToXXXメソッドでは、変換するバイト配列と、変換する最初のインデックスを指定する必要があります。 なお、BitConverter.ToStringメソッドは、各バイトをハイフンで連結した文字列を返します。

using System;

class Sample {
  static void Main()
  {
    byte[] bytes = new byte[] {0xff, 0xff, 0x80, 0x00};

    Console.WriteLine(BitConverter.ToString(bytes, 0));
    Console.WriteLine("{0}   0x{0:X8}", BitConverter.ToInt32(bytes, 0));
    Console.WriteLine("{0}   0x{0:X4}", BitConverter.ToInt16(bytes, 0));
    Console.WriteLine("{0}   0x{0:X4}", BitConverter.ToInt16(bytes, 2));
    Console.WriteLine(BitConverter.ToSingle(bytes, 0));

    Console.WriteLine();

    Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(42)));
    Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(1.0e+5)));
    Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(double.PositiveInfinity)));
    Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes('あ')));
  }
}
実行結果
FF-FF-80-00
8454143   0x0080FFFF
-1   0xFFFF
128   0x0080
1.184678E-38

2A-00-00-00
00-00-00-00-00-6A-F8-40
00-00-00-00-00-00-F0-7F
42-30

ただし、BitConverter.ToStringの逆の動作をするメソッド、つまりBitConverter.ToStringの出力形式と同じ文字列からバイト配列を取得するメソッドは存在しないため、自分で実装する必要があります。 また、この形式の文字列から基本型に直接変換するメソッドも用意されていないため、これも自分で実装する必要があります。

using System;

class Sample {
  static byte[] ToByteArray(string data)
  {
    // ハイフン '-' で分割
    string[] byteStrings = data.Split('-');

    // 配列に分割した文字列をバイト配列に変換
    return Array.ConvertAll(byteStrings, delegate(string s) {
      return Convert.ToByte(s, 16); // 16進形式の文字列からByteに変換
    });
  }

  static void Main()
  {
    string data = "FF-FF-80-00";

    Console.WriteLine("{0} => {1} 0x{1:X8}", data, BitConverter.ToInt32(ToByteArray(data), 0));

    data = "00-00-00-00-00-6A-F8-40";

    Console.WriteLine("{0} => {1}", data, BitConverter.ToDouble(ToByteArray(data), 0));
  }
}
実行結果
FF-FF-80-00 => 8454143 0x0080FFFF
00-00-00-00-00-6A-F8-40 => 100000

リファレンスでは明記されていませんが、結果として得られるバイト表現や変換結果は実行している環境のエンディアンなどにより変わると思われます。 現在実行している環境のエンディアンを調べるには、BitConverter.IsLittleEndianプロパティを参照します(ランタイム・システム・プラットフォームの情報 §.エンディアン)。 また、IPAddressクラスNetworkToHostOrderメソッドを使うことで整数型の値をネットワークバイトオーダー(ビッグエンディアン)からホストバイトオーダーに、HostToNetworkOrderメソッドを使うことでホストバイトオーダーからネットワークバイトオーダーに変換することが出来ます。

using System;
using System.Net;

class Sample {
  static void Main()
  {
    Console.WriteLine("IsLittleEndian: {0}", BitConverter.IsLittleEndian);

    Console.WriteLine("NetworkToHostOrder");
    Console.WriteLine("{0:X8} => {1:X8}", 16, IPAddress.NetworkToHostOrder(16));
    Console.WriteLine("{0:X8} => {1:X8}", -256, IPAddress.NetworkToHostOrder(-256));
    Console.WriteLine("HostToNetworkOrder");
    Console.WriteLine("{0:X8} => {1:X8}", 16, IPAddress.HostToNetworkOrder(16));
    Console.WriteLine("{0:X8} => {1:X8}", -256, IPAddress.HostToNetworkOrder(-256));
  }
}
実行結果
IsLittleEndian: True
NetworkToHostOrder
00000010 => 10000000
FFFFFF00 => 00FFFFFF
HostToNetworkOrder
00000010 => 10000000
FFFFFF00 => 00FFFFFF

BitConverterクラスのメソッドを使うことで基本型とバイト配列の相互変換を行うことができますが、独自に定義した構造体などの変換には対応していません。 構造体とバイト配列との相互変換についてはBinaryReader・BinaryWriterでの構造体の読み書きで解説しています。

このほかバイト配列に関する操作についてバイト列操作でも解説しています。

§4.2 文字列とバイト配列への/からの変換

Encodingクラスを使うことで、任意の文字コードを用いて文字列とバイト配列の変換を行うことが出来ます。 GetBytesメソッドで文字列からバイト配列、GetStringメソッドでバイト配列から文字列に変換できます。

using System;
using System.Text;

class Sample {
  static void Main()
  {
    Console.WriteLine(BitConverter.ToString(Encoding.ASCII.GetBytes("ABC")));
    Console.WriteLine(BitConverter.ToString(Encoding.UTF8.GetBytes("あいう")));
    Console.WriteLine(BitConverter.ToString(Encoding.GetEncoding(932).GetBytes("漢字")));

    Console.WriteLine(Encoding.ASCII.GetString(new byte[] {0x41, 0x42, 0x43}));
    Console.WriteLine(Encoding.UTF8.GetString(new byte[] {0xE3, 0x81, 0x82, 0xE3, 0x81, 0x84, 0xE3, 0x81, 0x86}));
    Console.WriteLine(Encoding.GetEncoding(932).GetString(new byte[] {0x8A, 0xBF, 0x8E, 0x9A}));
  }
}
実行結果
41-42-43
E3-81-82-E3-81-84-E3-81-86
8A-BF-8E-9A
ABC
あいう
漢字

§5 基本型間の変換

§5.1 Convertクラス

Convertクラスには、基本型間の変換を行うメソッドが用意されています。 これらのメソッドによる変換の結果は、キャストによる変換や、基本型のParseメソッドやToStringメソッドを直接呼び出す場合と概ね同じであるため、直接使う機会はあまりありません。 実数型から整数型への変換では、最近接偶数への丸め(MidpointRounding.ToEven)が行われます。

基本型間の変換が出来るメソッド
メソッド 動作
ToByte 文字列からByteへの変換
ToChar 文字列からCharへの変換
ToInt16 文字列からInt16への変換
ToInt32 文字列からInt32への変換
ToInt64 文字列からInt64への変換
ToSByte 文字列からSByteへの変換
ToUInt16 文字列からUInt16への変換
ToUInt32 文字列からUInt23への変換
ToUInt64 文字列からUInt64への変換
ToSingle 文字列からSingleへの変換
ToDouble 文字列からDoubleへの変換
ToDecimal 文字列からDecimalへの変換
ToBoolean 文字列からBooleanへの変換
ToDateTime 文字列からDateTimeへの変換
ToString 文字列からStringへの変換

このメソッドは、変換できない形式の場合はFormatException、変換した値が型の最大値・最小値を超える場合はOverflowException、変換が定義されていない場合(DateTimeからInt32など)はInvalidCastExceptionをスローします。

また、Convertクラスにはobjectから任意の型に変換するChangeTypeメソッドも用意されています。 このメソッドは変換後の型をTypeクラスまたはTypeCode列挙型で指定することが出来ます。 変換される型はIConvertibleインターフェイスを実装している必要があります(詳しくはユーザ定義の型変換で解説します)。 当然、変換できない型の場合には例外InvalidCastExceptionがスローされます。

以下の例は、Convertクラスのメソッドを使って基本型の型変換を行ったものです。

using System;

class Sample {
  static void Main()
  {
    int i1 = Convert.ToInt32(3.5);
    int i2 = Convert.ToInt32(4.5);

    Console.WriteLine(i1);
    Console.WriteLine(i2);

    double d = Convert.ToDouble(16);

    Console.WriteLine(d);

    object o1 = Convert.ChangeType("3.14", typeof(double));
    object o2 = Convert.ChangeType("3.14", TypeCode.Double);
    object o3 = Convert.ChangeType("72", typeof(int));
    object o4 = Convert.ChangeType("72", TypeCode.Int32);

    Console.WriteLine("{0} ({1})", o1, o1.GetType());
    Console.WriteLine("{0} ({1})", o2, o2.GetType());
    Console.WriteLine("{0} ({1})", o3, o3.GetType());
    Console.WriteLine("{0} ({1})", o4, o4.GetType());
  }
}
実行結果
4
4
16
3.14 (System.Double)
3.14 (System.Double)
72 (System.Int32)
72 (System.Int32)

§5.2 XmlConvertクラス

XmlConvertクラスは本来、XMLスキーマのデータ型との変換を行うために用意されてるものですが、ConvertクラスではサポートされていないGuidTimeSpanDateTimeOffsetと文字列との変換を行うことが出来ます。