暗黙的・明示的な型変換

暗黙の型変換

もっとも基本的な型変換に、代入時の暗黙の型変換があります。 例えば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);
  }
}
Option Strict On

Imports System

Class Sample
  Shared Sub Main()
    Dim i As Integer = 42
    Dim d As Double

    d = i ' IntegerからDoubleへの暗黙の型変換が行われる

    Console.WriteLine(i)
    Console.WriteLine(d)
  End Sub
End Class
実行結果
42
42

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

逆に、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 が不足していないかどうかを確認してください)
  }
}
Option Strict On

Imports System

Class Sample
  Shared Sub Main()
    Dim d As Double = 3.14
    Dim i As Integer

    i = d ' DoubleからIntegerへの縮小変換となるので暗黙の型変換はできない
    ' error BC30512: Option Strict On で 'Double' から 'Integer' への暗黙的な変換はできません。
  End Sub
End Class

この場合、キャスト構文や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);
  }
}
Option Strict On

Imports System

Class Sample
  Shared Sub Main()
    Dim d As Double = 3.14
    Dim i As Integer

    i = CInt(d) ' DoubleからIntegerへの型変換

    Console.WriteLine(d)
    Console.WriteLine(i)
  End Sub
End Class
実行結果
3.14
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()

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

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

文字列への/からの変換

文字列への変換 (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);
  }
}
Imports System

Class Sample
  Shared Sub Main()
    Dim i As Integer = 42
    Dim d As Double = 3.141592

    Dim s1 As String = i.ToString()
    Dim s2 As String = i.ToString("D8")
    Dim s3 As String = d.ToString()
    Dim s4 As String = d.ToString("F4")

    Console.WriteLine(s1)
    Console.WriteLine(s2)
    Console.WriteLine(s3)
    Console.WriteLine(s4)
  End Sub
End Class
実行結果
42
00000042
3.141592
3.1416

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

2進・10進・16進などの形式での文字列化については、ビット演算 §.基数を指定した数値から文字列への変換を参照してください。

文字列からの変換 (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);
  }
}
Imports System

Class Sample
  Shared Sub Main()
    Dim i1 As Integer = Integer.Parse("42")
    Dim i2 As Integer = Integer.Parse("000042")
    Dim d1 As Double = Double.Parse("3.14")
    Dim d2 As Double = Double.Parse("1.23E+3")

    Console.WriteLine(i1)
    Console.WriteLine(i2)
    Console.WriteLine(d1)
    Console.WriteLine(d2)
  End Sub
End Class
実行結果
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("変換できない値");
  }
}
Imports System

Class Sample
  Shared Sub Main()
    Dim i As Integer

    If Integer.TryParse("42", i) Then
      Console.WriteLine(i)
    Else
      Console.WriteLine("変換できない値")
    End If

    If Integer.TryParse("abc", i) Then
      Console.WriteLine(i)
    Else
      Console.WriteLine("変換できない値")
    End If

    If Integer.TryParse("01234567890123456789", i) Then
      Console.WriteLine(i)
    Else
      Console.WriteLine("変換できない値")
    End If

    Dim d As Double

    If Double.TryParse("3.14", d) Then
      Console.WriteLine(d)
    Else
      Console.WriteLine("変換できない値")
    End If

    If Double.TryParse("1.2345E+6789", d) Then
      Console.WriteLine(d)
    Else
      Console.WriteLine("変換できない値")
    End If
  End Sub
End Class
実行結果
42
変換できない値
変換できない値
3.14
変換できない値

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

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

16進・8進・2進数形式の文字列から整数型/整数型から文字列に変換するには、Convert.ToStringメソッドおよびConvert.ToInt32などのメソッドを使うことができます。 このメソッドでは、基数として2、8、10、16のいずれかを指定することができます。 これ以外の基数を指定すると例外ArgumentExceptionがスローされます。

このメソッドでは、符号付きの数値が許可されるかどうかなどの動作が基数によって変わります。 メソッドの詳しい動作についてはビット演算 §.基数を指定した文字列から数値への変換を参照してください。

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

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進数として変換
  }
}
Imports System

Class Sample
  Shared Sub 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進数として変換
  End Sub
End Class
実行結果
10000000
200
128
80

4
64
100
256

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

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

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('あ')));
  }
}
Imports System

Class Sample
  Shared Sub Main()
    Dim bytes() As Byte = New byte() {&hFF, &hFF, &h80, &h00}

    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("あ"c)))
  End Sub
End Class
実行結果
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));
  }
}
Imports System

Class Sample
  Shared Function ToByteArray(ByVal data As String) As Byte()
    ' ハイフン '-' で分割
    Dim byteStrings As String() = data.Split("-"c)

    ' 配列に分割した文字列をバイト配列に変換
    Return Array.ConvertAll(Of String, Byte)(byteStrings, AddressOf ToByteFromHex)
  End Function

  Shared Function ToByteFromHex(ByVal hex As String) As Byte
    ' 16進形式の文字列からByteに変換
    Return Convert.ToByte(hex, 16)
  End Function

  Shared Sub Main()
    Dim data As String = "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))
  End Sub
End Class
実行結果
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));
  }
}
Imports System
Imports System.Net

Class Sample
  Shared Sub 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))
  End Sub
End Class
実行結果
IsLittleEndian: True
NetworkToHostOrder
00000010 => 10000000
FFFFFF00 => 00FFFFFF
HostToNetworkOrder
00000010 => 10000000
FFFFFF00 => 00FFFFFF

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

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

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

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}));
  }
}
Imports System
Imports System.Text

Class Sample
  Shared Sub 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() {&h41, &h42, &h43}))
    Console.WriteLine(Encoding.UTF8.GetString(New Byte() {&hE3, &h81, &h82, &hE3, &h81, &h84, &hE3, &h81, &h86}))
    Console.WriteLine(Encoding.GetEncoding(932).GetString(New Byte() {&h8A, &hBF, &h8E, &h9A}))
  End Sub
End Class
実行結果
41-42-43
E3-81-82-E3-81-84-E3-81-86
8A-BF-8E-9A
ABC
あいう
漢字

基本型間の変換

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());
  }
}
Imports System

Class Sample
  Shared Sub Main()
    Dim i1 As Integer = Convert.ToInt32(3.5)
    Dim i2 As Integer = Convert.ToInt32(4.5)

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

    Dim d As Double = Convert.ToDouble(16)

    Console.WriteLine(d)

    Dim o1 As Object = Convert.ChangeType("3.14", GetType(Double))
    Dim o2 As Object = Convert.ChangeType("3.14", TypeCode.Double)
    Dim o3 As Object = Convert.ChangeType("72", GetType(Integer))
    Dim o4 As Object = 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())
  End Sub
End Class
実行結果
4
4
16
3.14 (System.Double)
3.14 (System.Double)
72 (System.Int32)
72 (System.Int32)

XmlConvertクラス

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