共用方式為


.NET 中的類型轉換

每個值都有相關聯的類型,其會定義屬性,例如配置給值的空間量、它可以擁有的可能值範圍,以及可供使用的成員。 許多值可以表示為一個以上的類型。 例如,值 4 可以表示為整數或浮點值。 類型轉換會在與舊型別的值相等的新型別中建立值,但不一定保留原始對象的識別(或確切值)。

.NET 會自動支援下列轉換:

  • 從衍生類別轉換成基類。 例如,這表示任何類別或結構的實例都可以轉換成 Object 實例。 此轉換不需要轉型或轉換運算符。

  • 從基類轉換回原始衍生類別。 在 C# 中,此轉換需要轉型運算元。 在 Visual Basic 中,如果 Option Strict 開啟,則需要 CType 運算符。

  • 從實作介面的類型轉換成表示該介面的介面物件。 此轉換過程不需要轉型或轉換運算子。

  • 從介面物件轉換回實作該介面的原始類型。 在 C# 中,此轉換需要轉型運算元。 在 Visual Basic 中,若 Option Strict 已啟用,則必須使用 CType 運算符。

除了這些自動轉換之外,.NET 還提供數個支援自定義類型轉換的功能。 這些包括下列各項:

  • Implicit 運算符,定義型別之間可用的擴充轉換。 如需詳細資訊,請參閱 隱含轉換與隱含運算子 一節。

  • Explicit 運算符,定義型別之間可用的縮小轉換。 如需詳細資訊,請參閱使用明確運算子 明確轉換一節。

  • IConvertible 介面,定義每個基底 .NET 數據類型的轉換。 如需詳細資訊,請參閱 IConvertible 介面 一節。

  • Convert 類別,提供一組方法,可在 IConvertible 介面中實作 方法。 如需詳細資訊,請參閱 轉換類別 一節。

  • TypeConverter 類別,這是可擴充以支援將指定型別轉換成任何其他型別的基類。 如需詳細資訊,請參閱 TypeConverter 類別 一節。

使用隱含運算子的隱式轉換

擴大轉換涉及從現有型別的數值創建新值,這個新值的範圍或成員清單更寬鬆,而現有型別則具有更有限的範圍或成員清單。 擴大轉換不會導致數據遺失(雖然它們可能會導致精確度遺失)。 因為數據無法遺失,編譯程式可以隱含或透明地處理轉換,而不需要使用明確的轉換方法或轉換運算符。

備註

雖然執行隱含轉換的程式代碼可以呼叫轉換方法或使用轉型運算符,但是支援隱含轉換的編譯程式不需要使用它們。

例如,Decimal 類型支援從 ByteCharInt16Int32Int64SByteUInt16UInt32UInt64 值的隱含轉換。 下列範例說明將值指派給 Decimal 變數的其中一些隱含轉換。

  byte byteValue = 16;
  short shortValue = -1024;
  int intValue = -1034000;
  long longValue = 1152921504606846976;
  ulong ulongValue = UInt64.MaxValue;

  decimal decimalValue;

  decimalValue = byteValue;
  Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                    byteValue.GetType().Name, decimalValue);

  decimalValue = shortValue;
  Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                    shortValue.GetType().Name, decimalValue);

  decimalValue = intValue;
  Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                    intValue.GetType().Name, decimalValue);

  decimalValue = longValue;
  Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                    longValue.GetType().Name, decimalValue);

decimalValue = ulongValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                  ulongValue.GetType().Name, decimalValue);
// The example displays the following output:
//    After assigning a Byte value, the Decimal value is 16.
//    After assigning a Int16 value, the Decimal value is -1024.
//    After assigning a Int32 value, the Decimal value is -1034000.
//    After assigning a Int64 value, the Decimal value is 1152921504606846976.
//    After assigning a UInt64 value, the Decimal value is 18446744073709551615.
Dim byteValue As Byte = 16
Dim shortValue As Short = -1024
Dim intValue As Integer = -1034000
Dim longValue As Long = CLng(1024 ^ 6)
Dim ulongValue As ULong = ULong.MaxValue

Dim decimalValue As Decimal

decimalValue = byteValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                  byteValue.GetType().Name, decimalValue)

decimalValue = shortValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                  shortValue.GetType().Name, decimalValue)

decimalValue = intValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                  intValue.GetType().Name, decimalValue)

decimalValue = longValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                  longValue.GetType().Name, decimalValue)

decimalValue = ulongValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
                  ulongValue.GetType().Name, decimalValue)
' The example displays the following output:
'    After assigning a Byte value, the Decimal value is 16.
'    After assigning a Int16 value, the Decimal value is -1024.
'    After assigning a Int32 value, the Decimal value is -1034000.
'    After assigning a Int64 value, the Decimal value is 1152921504606846976.
'    After assigning a UInt64 value, the Decimal value is 18446744073709551615.

如果特定語言編譯程式支援自定義運算元,您也可以在自己的自定義類型中定義隱含轉換。 下列範例提供一個名為 ByteWithSign 且使用正負號和絕對值表示法的已簽名位元組資料類型的部分實作。 它支援將 ByteSByte 值隱含轉換成 ByteWithSign 值。

public struct ByteWithSign
{
    private SByte signValue;
    private Byte value;

    public static implicit operator ByteWithSign(SByte value)
    {
        ByteWithSign newValue;
        newValue.signValue = (SByte)Math.Sign(value);
        newValue.value = (byte)Math.Abs(value);
        return newValue;
    }

    public static implicit operator ByteWithSign(Byte value)
    {
        ByteWithSign newValue;
        newValue.signValue = 1;
        newValue.value = value;
        return newValue;
    }

    public override string ToString()
    {
        return (signValue * value).ToString();
    }
}
Public Structure ImplicitByteWithSign
    Private signValue As SByte
    Private value As Byte

    Public Overloads Shared Widening Operator CType(value As SByte) As ImplicitByteWithSign
        Dim newValue As ImplicitByteWithSign
        newValue.signValue = CSByte(Math.Sign(value))
        newValue.value = CByte(Math.Abs(value))
        Return newValue
    End Operator

    Public Overloads Shared Widening Operator CType(value As Byte) As ImplicitByteWithSign
        Dim NewValue As ImplicitByteWithSign
        newValue.signValue = 1
        newValue.value = value
        Return newValue
    End Operator

    Public Overrides Function ToString() As String
        Return (signValue * value).ToString()
    End Function
End Structure

然後,用戶端程式代碼可以宣告 ByteWithSign 變數,並將其指派 ByteSByte 值,而不需執行任何明確轉換或使用任何轉換運算符,如下列範例所示。

SByte sbyteValue = -120;
ByteWithSign value = sbyteValue;
Console.WriteLine(value);
value = Byte.MaxValue;
Console.WriteLine(value);
// The example displays the following output:
//       -120
//       255
Dim sbyteValue As SByte = -120
Dim value As ImplicitByteWithSign = sbyteValue
Console.WriteLine(value.ToString())
value = Byte.MaxValue
Console.WriteLine(value.ToString())
' The example displays the following output:
'       -120
'       255

使用明確運算子進行明確轉換

縮小轉換牽涉到從現有型別的值建立新的值,這個值的範圍大於目標類型或更大的成員清單。 因為縮小轉換可能會導致資料遺失,編譯程式通常需要透過呼叫轉換方法或轉換運算符來明確轉換。 也就是說,轉換必須在開發人員程式代碼中明確處理。

備註

要求轉換方法或轉換運算符縮小轉換的主要目的是讓開發人員知道數據遺失或 OverflowException 的可能性,以便在程式代碼中加以處理。 不過,某些編譯程式可以放寬這項需求。 例如,在 Visual Basic 中,如果 Option Strict 已關閉(其預設設定),Visual Basic 編譯程式會嘗試隱含執行縮小轉換。

例如,UInt32Int64UInt64 數據類型的範圍超過 Int32 數據類型,如下表所示。

類型 與 Int32 範圍的比較
Int64 Int64.MaxValue 大於 Int32.MaxValue,且 Int64.MinValue 小於(負範圍大於)Int32.MinValue
UInt32 UInt32.MaxValue 大於 Int32.MaxValue
UInt64 UInt64.MaxValue 大於 Int32.MaxValue

為了處理這類縮小轉換,.NET 允許類型定義 Explicit 運算符。 接著,個別語言編譯程式可以使用自己的語法實作此運算符,或呼叫 Convert 類別的成員來執行轉換。 (如需 Convert 類別的詳細資訊,請參閱本主題稍後的 轉換類別)。下列範例說明如何使用語言功能來處理這些可能超出範圍的整數值的明確轉換,以取得 Int32 值。

long number1 = int.MaxValue + 20L;
uint number2 = int.MaxValue - 1000;
ulong number3 = int.MaxValue;

int intNumber;

try
{
    intNumber = checked((int)number1);
    Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
                      number1.GetType().Name, intNumber);
}
catch (OverflowException)
{
    if (number1 > int.MaxValue)
        Console.WriteLine($"Conversion failed: {number1} exceeds {int.MaxValue}.");
    else
        Console.WriteLine($"Conversion failed: {number1} is less than {int.MinValue}.");
}

try
{
    intNumber = checked((int)number2);
    Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
                      number2.GetType().Name, intNumber);
}
catch (OverflowException)
{
    Console.WriteLine($"Conversion failed: {number2} exceeds {int.MaxValue}.");
}

try
{
    intNumber = checked((int)number3);
    Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
                      number3.GetType().Name, intNumber);
}
catch (OverflowException)
{
    Console.WriteLine($"Conversion failed: {number1} exceeds {int.MaxValue}.");
}

// The example displays the following output:
//    Conversion failed: 2147483667 exceeds 2147483647.
//    After assigning a UInt32 value, the Integer value is 2147482647.
//    After assigning a UInt64 value, the Integer value is 2147483647.
Dim number1 As Long = Integer.MaxValue + 20L
Dim number2 As UInteger = Integer.MaxValue - 1000
Dim number3 As ULong = Integer.MaxValue

Dim intNumber As Integer

Try
    intNumber = CInt(number1)
    Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
                        number1.GetType().Name, intNumber)
Catch e As OverflowException
    If number1 > Integer.MaxValue Then
        Console.WriteLine("Conversion failed: {0} exceeds {1}.",
                                          number1, Integer.MaxValue)
    Else
        Console.WriteLine("Conversion failed: {0} is less than {1}.\n",
                                          number1, Integer.MinValue)
    End If
End Try

Try
    intNumber = CInt(number2)
    Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
                        number2.GetType().Name, intNumber)
Catch e As OverflowException
    Console.WriteLine("Conversion failed: {0} exceeds {1}.",
                                      number2, Integer.MaxValue)
End Try

Try
    intNumber = CInt(number3)
    Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
                        number3.GetType().Name, intNumber)
Catch e As OverflowException
    Console.WriteLine("Conversion failed: {0} exceeds {1}.",
                                      number1, Integer.MaxValue)
End Try
' The example displays the following output:
'    Conversion failed: 2147483667 exceeds 2147483647.
'    After assigning a UInt32 value, the Integer value is 2147482647.
'    After assigning a UInt64 value, the Integer value is 2147483647.

明確轉換可能會以不同的語言產生不同的結果,而且這些結果可能會與對應 Convert 方法傳回的值不同。 例如,如果 Double 值 12.63251 轉換成 Int32,則 Visual Basic CInt 方法和 .NET Convert.ToInt32(Double) 方法會四捨五入 Double 以傳回 13 的值,但 C# (int) 運算符會截斷 Double 以傳回 12 的值。 同樣地,C# (int) 運算符不支援布爾值到整數轉換,但 Visual Basic CInt 方法會將 true 值轉換成 -1。 另一方面,Convert.ToInt32(Boolean) 方法會將 true 值轉換成 1。

大部分的編譯程式都允許以已核取或未核取的方式執行明確轉換。 執行檢查的轉換時,當要轉換的類型值超出目標類型範圍時,就會擲回 OverflowException。 在相同的條件下執行未檢查的轉換時,轉換可能不會拋出例外,但確切的行為可能變得無法預測,而且可能會產生不正確的值。

備註

在 C# 中,檢查的轉換可以搭配轉換運算子搭配使用 checked 關鍵詞,或藉由指定 /checked+ 編譯程式選項來執行。 相反地,使用 unchecked 關鍵詞與轉換運算符,或指定 /checked- 編譯程式選項,即可執行未核取的轉換。 根據預設,會取消核取明確轉換。 在 Visual Basic 中,您可以清除專案 [進階 編譯程式設定] 對話框中的 [移除整數溢位檢查] 複選框,或指定 [/removeintchecks- 編譯程式] 選項,以執行核取轉換。 相反地,在專案的 [ 階編譯程式設定] 對話框中選取 [移除整數溢位檢查],或指定 [/removeintchecks+ 編譯程式] 選項,即可執行未核取的轉換。 根據預設,會檢查明確的轉換。

下列 C# 範例使用 checkedunchecked 關鍵詞來說明當 Byte 範圍以外的值轉換成 Byte時的行為差異。 核取的轉換會擲回例外狀況,但未核取的轉換會將 Byte.MaxValue 指派給 Byte 變數。

int largeValue = Int32.MaxValue;
byte newValue;

try
{
    newValue = unchecked((byte)largeValue);
    Console.WriteLine("Converted the {0} value {1} to the {2} value {3}.",
                      largeValue.GetType().Name, largeValue,
                      newValue.GetType().Name, newValue);
}
catch (OverflowException)
{
    Console.WriteLine($"{largeValue} is outside the range of the Byte data type.");
}

try
{
    newValue = checked((byte)largeValue);
    Console.WriteLine("Converted the {0} value {1} to the {2} value {3}.",
                      largeValue.GetType().Name, largeValue,
                      newValue.GetType().Name, newValue);
}
catch (OverflowException)
{
    Console.WriteLine($"{largeValue} is outside the range of the Byte data type.");
}
// The example displays the following output:
//    Converted the Int32 value 2147483647 to the Byte value 255.
//    2147483647 is outside the range of the Byte data type.

如果特定語言編譯程式支援自定義多載運算元,您也可以在自己的自定義類型中定義明確的轉換。 下列範例提供了部分的已簽名位元組資料類型 ByteWithSign 的實作,該類型使用符號-數量表示法。 它支援將 Int32UInt32 值明確轉換成 ByteWithSign 值。

public struct ByteWithSignE
{
    private SByte signValue;
    private Byte value;

    private const byte MaxValue = byte.MaxValue;
    private const int MinValue = -1 * byte.MaxValue;

    public static explicit operator ByteWithSignE(int value)
    {
        // Check for overflow.
        if (value > ByteWithSignE.MaxValue || value < ByteWithSignE.MinValue)
            throw new OverflowException(String.Format("'{0}' is out of range of the ByteWithSignE data type.",
                                                      value));

        ByteWithSignE newValue;
        newValue.signValue = (SByte)Math.Sign(value);
        newValue.value = (byte)Math.Abs(value);
        return newValue;
    }

    public static explicit operator ByteWithSignE(uint value)
    {
        if (value > ByteWithSignE.MaxValue)
            throw new OverflowException(String.Format("'{0}' is out of range of the ByteWithSignE data type.",
                                                      value));

        ByteWithSignE newValue;
        newValue.signValue = 1;
        newValue.value = (byte)value;
        return newValue;
    }

    public override string ToString()
    {
        return (signValue * value).ToString();
    }
}
Public Structure ByteWithSign
    Private signValue As SByte
    Private value As Byte

    Private Const MaxValue As Byte = Byte.MaxValue
    Private Const MinValue As Integer = -1 * Byte.MaxValue

    Public Overloads Shared Narrowing Operator CType(value As Integer) As ByteWithSign
        ' Check for overflow.
        If value > ByteWithSign.MaxValue Or value < ByteWithSign.MinValue Then
            Throw New OverflowException(String.Format("'{0}' is out of range of the ByteWithSign data type.", value))
        End If

        Dim newValue As ByteWithSign

        newValue.signValue = CSByte(Math.Sign(value))
        newValue.value = CByte(Math.Abs(value))
        Return newValue
    End Operator

    Public Overloads Shared Narrowing Operator CType(value As UInteger) As ByteWithSign
        If value > ByteWithSign.MaxValue Then
            Throw New OverflowException(String.Format("'{0}' is out of range of the ByteWithSign data type.", value))
        End If

        Dim NewValue As ByteWithSign

        newValue.signValue = 1
        newValue.value = CByte(value)
        Return newValue
    End Operator

    Public Overrides Function ToString() As String
        Return (signValue * value).ToString()
    End Function
End Structure

然後,客戶端程式碼可以宣告一個 ByteWithSign 變數,並將 Int32UInt32 值指派給該變數,前提是指派中包含轉型運算子或轉換方法,如下列範例所示。

ByteWithSignE value;

try
{
    int intValue = -120;
    value = (ByteWithSignE)intValue;
    Console.WriteLine(value);
}
catch (OverflowException e)
{
    Console.WriteLine(e.Message);
}

try
{
    uint uintValue = 1024;
    value = (ByteWithSignE)uintValue;
    Console.WriteLine(value);
}
catch (OverflowException e)
{
    Console.WriteLine(e.Message);
}
// The example displays the following output:
//       -120
//       '1024' is out of range of the ByteWithSignE data type.
Dim value As ByteWithSign

Try
    Dim intValue As Integer = -120
    value = CType(intValue, ByteWithSign)
    Console.WriteLine(value)
Catch e As OverflowException
    Console.WriteLine(e.Message)
End Try

Try
    Dim uintValue As UInteger = 1024
    value = CType(uintValue, ByteWithSign)
    Console.WriteLine(value)
Catch e As OverflowException
    Console.WriteLine(e.Message)
End Try
' The example displays the following output:
'       -120
'       '1024' is out of range of the ByteWithSign data type.

IConvertible 介面

為了支援將任何類型轉換成 Common Language Runtime 基底類型,.NET 提供 IConvertible 介面。 需要實作類型才能提供下列專案:

  • 傳回實作型別 TypeCode 的方法。

  • 將實作型別轉換成每個 Common Language Runtime 基底類型的方法(BooleanByteDateTimeDecimalDouble等等)。

  • 一般化轉換方法,可將實作型別的實例轉換成另一個指定的型別。 不支援的轉換應該會拋出 InvalidCastException

每個 Common Language Runtime 基底類型(也就是 BooleanByteCharDateTimeDecimalDoubleInt16Int32Int64SByteSingleStringUInt16UInt32UInt64),以及 DBNullEnum 類型,實作 IConvertible 介面。 不過,這些是明確的介面實作;轉換方法只能透過 IConvertible 介面變數呼叫,如下列範例所示。 本範例會將 Int32 值轉換成其對等 Char 值。

int codePoint = 1067;
IConvertible iConv = codePoint;
char ch = iConv.ToChar(null);
Console.WriteLine($"Converted {codePoint} to {ch}.");
Dim codePoint As Integer = 1067
Dim iConv As IConvertible = codePoint
Dim ch As Char = iConv.ToChar(Nothing)
Console.WriteLine("Converted {0} to {1}.", codePoint, ch)

由於需要在介面上而不是在實作類型上呼叫轉換方法,這使得明確的介面實作相對昂貴。 相反地,建議您呼叫 Convert 類別的適當成員,以在 Common Language Runtime 基底類型之間轉換。 如需詳細資訊,請參閱下一節,轉換類別

備註

除了 .NET 所提供的 IConvertible 介面和 Convert 類別之外,個別語言也可能提供執行轉換的方式。 例如,C# 使用轉型運算符;Visual Basic 使用編譯程式實作的轉換函式,例如 CTypeCIntDirectCast

在大多數情況下,IConvertible 介面的設計目的是支援 .NET 中基底類型之間的轉換。 不過,介面也可以由自定義類型實作,以支援將該類型轉換成其他自定義類型。 如需詳細資訊,請參閱本主題稍後 使用 ChangeType 方法 自定義轉換一節。

Convert 類別

雖然可以呼叫每個基底類型的 IConvertible 介面實作來執行類型轉換,但呼叫 System.Convert 類別的方法是建議的語言中性方法,以便從一個基底類型轉換成另一個基底類型。 此外,Convert.ChangeType(Object, Type, IFormatProvider) 方法可用來從指定的自定義類型轉換成另一種類型。

基底類型之間的轉換

Convert 類別提供語言中性的方式來執行基底類型之間的轉換,而且適用於所有以 Common Language Runtime 為目標的語言。 它提供一組完整的方法來擴大和縮小轉換,並針對不支援的轉換擲回 InvalidCastException(例如將 DateTime 值轉換成整數值)。 縮小轉換會在檢查的內容中執行,如果轉換失敗,則會擲回 OverflowException

這很重要

由於 Convert 類別包含用來轉換至與從每個基底類型的方法,因此不需要呼叫每個基底類型的 IConvertible 明確介面實作。

下列範例說明如何使用 System.Convert 類別,在 .NET 基底類型之間執行數個擴大和縮小轉換。

// Convert an Int32 value to a Decimal (a widening conversion).
int integralValue = 12534;
decimal decimalValue = Convert.ToDecimal(integralValue);
Console.WriteLine("Converted the {0} value {1} to " +
                                  "the {2} value {3:N2}.",
                                  integralValue.GetType().Name,
                                  integralValue,
                                  decimalValue.GetType().Name,
                                  decimalValue);
// Convert a Byte value to an Int32 value (a widening conversion).
byte byteValue = Byte.MaxValue;
int integralValue2 = Convert.ToInt32(byteValue);
Console.WriteLine("Converted the {0} value {1} to " +
                                  "the {2} value {3:G}.",
                                  byteValue.GetType().Name,
                                  byteValue,
                                  integralValue2.GetType().Name,
                                  integralValue2);

// Convert a Double value to an Int32 value (a narrowing conversion).
double doubleValue = 16.32513e12;
try
{
    long longValue = Convert.ToInt64(doubleValue);
    Console.WriteLine("Converted the {0} value {1:E} to " +
                                      "the {2} value {3:N0}.",
                                      doubleValue.GetType().Name,
                                      doubleValue,
                                      longValue.GetType().Name,
                                      longValue);
}
catch (OverflowException)
{
    Console.WriteLine("Unable to convert the {0:E} value {1}.",
                                      doubleValue.GetType().Name, doubleValue);
}

// Convert a signed byte to a byte (a narrowing conversion).
sbyte sbyteValue = -16;
try
{
    byte byteValue2 = Convert.ToByte(sbyteValue);
    Console.WriteLine("Converted the {0} value {1} to " +
                                      "the {2} value {3:G}.",
                                      sbyteValue.GetType().Name,
                                      sbyteValue,
                                      byteValue2.GetType().Name,
                                      byteValue2);
}
catch (OverflowException)
{
    Console.WriteLine("Unable to convert the {0} value {1}.",
                                      sbyteValue.GetType().Name, sbyteValue);
}
// The example displays the following output:
//       Converted the Int32 value 12534 to the Decimal value 12,534.00.
//       Converted the Byte value 255 to the Int32 value 255.
//       Converted the Double value 1.632513E+013 to the Int64 value 16,325,130,000,000.
//       Unable to convert the SByte value -16.
' Convert an Int32 value to a Decimal (a widening conversion).
Dim integralValue As Integer = 12534
Dim decimalValue As Decimal = Convert.ToDecimal(integralValue)
Console.WriteLine("Converted the {0} value {1} to the {2} value {3:N2}.",
                  integralValue.GetType().Name,
                  integralValue,
                  decimalValue.GetType().Name,
                  decimalValue)

' Convert a Byte value to an Int32 value (a widening conversion).
Dim byteValue As Byte = Byte.MaxValue
Dim integralValue2 As Integer = Convert.ToInt32(byteValue)
Console.WriteLine("Converted the {0} value {1} to " +
                                  "the {2} value {3:G}.",
                                  byteValue.GetType().Name,
                                  byteValue,
                                  integralValue2.GetType().Name,
                                  integralValue2)

' Convert a Double value to an Int32 value (a narrowing conversion).
Dim doubleValue As Double = 16.32513e12
Try
    Dim longValue As Long = Convert.ToInt64(doubleValue)
    Console.WriteLine("Converted the {0} value {1:E} to " +
                                      "the {2} value {3:N0}.",
                                      doubleValue.GetType().Name,
                                      doubleValue,
                                      longValue.GetType().Name,
                                      longValue)
Catch e As OverflowException
    Console.WriteLine("Unable to convert the {0:E} value {1}.",
                                      doubleValue.GetType().Name, doubleValue)
End Try

' Convert a signed byte to a byte (a narrowing conversion).     
Dim sbyteValue As SByte = -16
Try
    Dim byteValue2 As Byte = Convert.ToByte(sbyteValue)
    Console.WriteLine("Converted the {0} value {1} to " +
                                      "the {2} value {3:G}.",
                                      sbyteValue.GetType().Name,
                                      sbyteValue,
                                      byteValue2.GetType().Name,
                                      byteValue2)
Catch e As OverflowException
    Console.WriteLine("Unable to convert the {0} value {1}.",
                                      sbyteValue.GetType().Name, sbyteValue)
End Try
' The example displays the following output:
'       Converted the Int32 value 12534 to the Decimal value 12,534.00.
'       Converted the Byte value 255 to the Int32 value 255.
'       Converted the Double value 1.632513E+013 to the Int64 value 16,325,130,000,000.
'       Unable to convert the SByte value -16.

在某些情況下,特別是在從浮點值轉換和轉換時,即使它不會擲回 OverflowException,轉換也可能牽涉到精確度的遺失。 下列範例說明此精確度損失。 在第一個案例中,當 Decimal 值轉換成 Double時,其有效位數會較低(有效位數較少)。 在第二個案例中,Double 值會四捨五入為 42.72 到 43,以完成轉換。

double doubleValue;

// Convert a Double to a Decimal.
decimal decimalValue = 13956810.96702888123451471211m;
doubleValue = Convert.ToDouble(decimalValue);
Console.WriteLine($"{decimalValue} converted to {doubleValue}.");

doubleValue = 42.72;
try
{
    int integerValue = Convert.ToInt32(doubleValue);
    Console.WriteLine($"{doubleValue} converted to {integerValue}.");
}
catch (OverflowException)
{
    Console.WriteLine($"Unable to convert {doubleValue} to an integer.");
}
// The example displays the following output:
//       13956810.96702888123451471211 converted to 13956810.9670289.
//       42.72 converted to 43.
Dim doubleValue As Double

' Convert a Double to a Decimal.
Dim decimalValue As Decimal = 13956810.96702888123451471211d
doubleValue = Convert.ToDouble(decimalValue)
Console.WriteLine("{0} converted to {1}.", decimalValue, doubleValue)

doubleValue = 42.72
Try
    Dim integerValue As Integer = Convert.ToInt32(doubleValue)
    Console.WriteLine("{0} converted to {1}.",
                                      doubleValue, integerValue)
Catch e As OverflowException
    Console.WriteLine("Unable to convert {0} to an integer.",
                                      doubleValue)
End Try
' The example displays the following output:
'       13956810.96702888123451471211 converted to 13956810.9670289.
'       42.72 converted to 43.

如需列出 Convert 類別所支援之擴大和縮小轉換的數據表,請參閱 類型轉換資料表

使用 ChangeType 方法進行自定義轉換

除了支援轉換至每個基底類型之外,Convert 類別可用來將自定義類型轉換成一或多個預先定義的類型。 這個轉換是由 Convert.ChangeType(Object, Type, IFormatProvider) 方法執行的,並且它會封裝對 value 參數的 IConvertible.ToType 方法的呼叫。 這表示 value 參數所代表的對象必須提供 IConvertible 介面的實作。

備註

由於 Convert.ChangeType(Object, Type)Convert.ChangeType(Object, Type, IFormatProvider) 方法會使用 Type 物件來指定 value 轉換的目標類型,所以可用來對編譯時期類型未知的物件執行動態轉換。 不過,請注意,valueIConvertible 實作仍必須支援此轉換。

下列範例說明 IConvertible 介面的可能實作,可讓 TemperatureCelsius 對象轉換成 TemperatureFahrenheit 物件,反之亦然。 此範例會定義基類 Temperature,這個基類會實作 IConvertible 介面,並覆寫 Object.ToString 方法。 衍生的 TemperatureCelsiusTemperatureFahrenheit 類別均會覆寫基類的 ToTypeToString 方法。

using System;

public abstract class Temperature : IConvertible
{
    protected decimal temp;

    public Temperature(decimal temperature)
    {
        this.temp = temperature;
    }

    public decimal Value
    {
        get { return this.temp; }
        set { this.temp = value; }
    }

    public override string ToString()
    {
        return temp.ToString(null as IFormatProvider) + "º";
    }

    // IConvertible implementations.
    public TypeCode GetTypeCode()
    {
        return TypeCode.Object;
    }

    public bool ToBoolean(IFormatProvider provider)
    {
        throw new InvalidCastException(String.Format("Temperature-to-Boolean conversion is not supported."));
    }

    public byte ToByte(IFormatProvider provider)
    {
        if (temp < Byte.MinValue || temp > Byte.MaxValue)
            throw new OverflowException(String.Format("{0} is out of range of the Byte data type.", temp));
        else
            return (byte)temp;
    }

    public char ToChar(IFormatProvider provider)
    {
        throw new InvalidCastException("Temperature-to-Char conversion is not supported.");
    }

    public DateTime ToDateTime(IFormatProvider provider)
    {
        throw new InvalidCastException("Temperature-to-DateTime conversion is not supported.");
    }

    public decimal ToDecimal(IFormatProvider provider)
    {
        return temp;
    }

    public double ToDouble(IFormatProvider provider)
    {
        return (double)temp;
    }

    public short ToInt16(IFormatProvider provider)
    {
        if (temp < Int16.MinValue || temp > Int16.MaxValue)
            throw new OverflowException(String.Format("{0} is out of range of the Int16 data type.", temp));
        else
            return (short)Math.Round(temp);
    }

    public int ToInt32(IFormatProvider provider)
    {
        if (temp < Int32.MinValue || temp > Int32.MaxValue)
            throw new OverflowException(String.Format("{0} is out of range of the Int32 data type.", temp));
        else
            return (int)Math.Round(temp);
    }

    public long ToInt64(IFormatProvider provider)
    {
        if (temp < Int64.MinValue || temp > Int64.MaxValue)
            throw new OverflowException(String.Format("{0} is out of range of the Int64 data type.", temp));
        else
            return (long)Math.Round(temp);
    }

    public sbyte ToSByte(IFormatProvider provider)
    {
        if (temp < SByte.MinValue || temp > SByte.MaxValue)
            throw new OverflowException(String.Format("{0} is out of range of the SByte data type.", temp));
        else
            return (sbyte)temp;
    }

    public float ToSingle(IFormatProvider provider)
    {
        return (float)temp;
    }

    public virtual string ToString(IFormatProvider provider)
    {
        return temp.ToString(provider) + "°";
    }

    // If conversionType is implemented by another IConvertible method, call it.
    public virtual object ToType(Type conversionType, IFormatProvider provider)
    {
        switch (Type.GetTypeCode(conversionType))
        {
            case TypeCode.Boolean:
                return this.ToBoolean(provider);
            case TypeCode.Byte:
                return this.ToByte(provider);
            case TypeCode.Char:
                return this.ToChar(provider);
            case TypeCode.DateTime:
                return this.ToDateTime(provider);
            case TypeCode.Decimal:
                return this.ToDecimal(provider);
            case TypeCode.Double:
                return this.ToDouble(provider);
            case TypeCode.Empty:
                throw new NullReferenceException("The target type is null.");
            case TypeCode.Int16:
                return this.ToInt16(provider);
            case TypeCode.Int32:
                return this.ToInt32(provider);
            case TypeCode.Int64:
                return this.ToInt64(provider);
            case TypeCode.Object:
                // Leave conversion of non-base types to derived classes.
                throw new InvalidCastException(String.Format("Cannot convert from Temperature to {0}.",
                                               conversionType.Name));
            case TypeCode.SByte:
                return this.ToSByte(provider);
            case TypeCode.Single:
                return this.ToSingle(provider);
            case TypeCode.String:
                IConvertible iconv = this;
                return iconv.ToString(provider);
            case TypeCode.UInt16:
                return this.ToUInt16(provider);
            case TypeCode.UInt32:
                return this.ToUInt32(provider);
            case TypeCode.UInt64:
                return this.ToUInt64(provider);
            default:
                throw new InvalidCastException("Conversion not supported.");
        }
    }

    public ushort ToUInt16(IFormatProvider provider)
    {
        if (temp < UInt16.MinValue || temp > UInt16.MaxValue)
            throw new OverflowException(String.Format("{0} is out of range of the UInt16 data type.", temp));
        else
            return (ushort)Math.Round(temp);
    }

    public uint ToUInt32(IFormatProvider provider)
    {
        if (temp < UInt32.MinValue || temp > UInt32.MaxValue)
            throw new OverflowException(String.Format("{0} is out of range of the UInt32 data type.", temp));
        else
            return (uint)Math.Round(temp);
    }

    public ulong ToUInt64(IFormatProvider provider)
    {
        if (temp < UInt64.MinValue || temp > UInt64.MaxValue)
            throw new OverflowException(String.Format("{0} is out of range of the UInt64 data type.", temp));
        else
            return (ulong)Math.Round(temp);
    }
}

public class TemperatureCelsius : Temperature, IConvertible
{
    public TemperatureCelsius(decimal value) : base(value)
    {
    }

    // Override ToString methods.
    public override string ToString()
    {
        return this.ToString(null);
    }

    public override string ToString(IFormatProvider provider)
    {
        return temp.ToString(provider) + "°C";
    }

    // If conversionType is a implemented by another IConvertible method, call it.
    public override object ToType(Type conversionType, IFormatProvider provider)
    {
        // For non-objects, call base method.
        if (Type.GetTypeCode(conversionType) != TypeCode.Object)
        {
            return base.ToType(conversionType, provider);
        }
        else
        {
            if (conversionType.Equals(typeof(TemperatureCelsius)))
                return this;
            else if (conversionType.Equals(typeof(TemperatureFahrenheit)))
                return new TemperatureFahrenheit((decimal)this.temp * 9 / 5 + 32);
            else
                throw new InvalidCastException(String.Format("Cannot convert from Temperature to {0}.",
                                               conversionType.Name));
        }
    }
}

public class TemperatureFahrenheit : Temperature, IConvertible
{
    public TemperatureFahrenheit(decimal value) : base(value)
    {
    }

    // Override ToString methods.
    public override string ToString()
    {
        return this.ToString(null);
    }

    public override string ToString(IFormatProvider provider)
    {
        return temp.ToString(provider) + "°F";
    }

    public override object ToType(Type conversionType, IFormatProvider provider)
    {
        // For non-objects, call base method.
        if (Type.GetTypeCode(conversionType) != TypeCode.Object)
        {
            return base.ToType(conversionType, provider);
        }
        else
        {
            // Handle conversion between derived classes.
            if (conversionType.Equals(typeof(TemperatureFahrenheit)))
                return this;
            else if (conversionType.Equals(typeof(TemperatureCelsius)))
                return new TemperatureCelsius((decimal)(this.temp - 32) * 5 / 9);
            // Unspecified object type: throw an InvalidCastException.
            else
                throw new InvalidCastException(String.Format("Cannot convert from Temperature to {0}.",
                                               conversionType.Name));
        }
    }
}
Public MustInherit Class Temperature
    Implements IConvertible

    Protected temp As Decimal

    Public Sub New(temperature As Decimal)
        Me.temp = temperature
    End Sub

    Public Property Value As Decimal
        Get
            Return Me.temp
        End Get
        Set
            Me.temp = Value
        End Set
    End Property

    Public Overrides Function ToString() As String
        Return temp.ToString() & "º"
    End Function

    ' IConvertible implementations.
    Public Function GetTypeCode() As TypeCode Implements IConvertible.GetTypeCode
        Return TypeCode.Object
    End Function

    Public Function ToBoolean(provider As IFormatProvider) As Boolean Implements IConvertible.ToBoolean
        Throw New InvalidCastException(String.Format("Temperature-to-Boolean conversion is not supported."))
    End Function

    Public Function ToByte(provider As IFormatProvider) As Byte Implements IConvertible.ToByte
        If temp < Byte.MinValue Or temp > Byte.MaxValue Then
            Throw New OverflowException(String.Format("{0} is out of range of the Byte data type.", temp))
        Else
            Return CByte(temp)
        End If
    End Function

    Public Function ToChar(provider As IFormatProvider) As Char Implements IConvertible.ToChar
        Throw New InvalidCastException("Temperature-to-Char conversion is not supported.")
    End Function

    Public Function ToDateTime(provider As IFormatProvider) As DateTime Implements IConvertible.ToDateTime
        Throw New InvalidCastException("Temperature-to-DateTime conversion is not supported.")
    End Function

    Public Function ToDecimal(provider As IFormatProvider) As Decimal Implements IConvertible.ToDecimal
        Return temp
    End Function

    Public Function ToDouble(provider As IFormatProvider) As Double Implements IConvertible.ToDouble
        Return CDbl(temp)
    End Function

    Public Function ToInt16(provider As IFormatProvider) As Int16 Implements IConvertible.ToInt16
        If temp < Int16.MinValue Or temp > Int16.MaxValue Then
            Throw New OverflowException(String.Format("{0} is out of range of the Int16 data type.", temp))
        End If
        Return CShort(Math.Round(temp))
    End Function

    Public Function ToInt32(provider As IFormatProvider) As Int32 Implements IConvertible.ToInt32
        If temp < Int32.MinValue Or temp > Int32.MaxValue Then
            Throw New OverflowException(String.Format("{0} is out of range of the Int32 data type.", temp))
        End If
        Return CInt(Math.Round(temp))
    End Function

    Public Function ToInt64(provider As IFormatProvider) As Int64 Implements IConvertible.ToInt64
        If temp < Int64.MinValue Or temp > Int64.MaxValue Then
            Throw New OverflowException(String.Format("{0} is out of range of the Int64 data type.", temp))
        End If
        Return CLng(Math.Round(temp))
    End Function

    Public Function ToSByte(provider As IFormatProvider) As SByte Implements IConvertible.ToSByte
        If temp < SByte.MinValue Or temp > SByte.MaxValue Then
            Throw New OverflowException(String.Format("{0} is out of range of the SByte data type.", temp))
        Else
            Return CSByte(temp)
        End If
    End Function

    Public Function ToSingle(provider As IFormatProvider) As Single Implements IConvertible.ToSingle
        Return CSng(temp)
    End Function

    Public Overridable Overloads Function ToString(provider As IFormatProvider) As String Implements IConvertible.ToString
        Return temp.ToString(provider) & " °C"
    End Function

    ' If conversionType is a implemented by another IConvertible method, call it.
    Public Overridable Function ToType(conversionType As Type, provider As IFormatProvider) As Object Implements IConvertible.ToType
        Select Case Type.GetTypeCode(conversionType)
            Case TypeCode.Boolean
                Return Me.ToBoolean(provider)
            Case TypeCode.Byte
                Return Me.ToByte(provider)
            Case TypeCode.Char
                Return Me.ToChar(provider)
            Case TypeCode.DateTime
                Return Me.ToDateTime(provider)
            Case TypeCode.Decimal
                Return Me.ToDecimal(provider)
            Case TypeCode.Double
                Return Me.ToDouble(provider)
            Case TypeCode.Empty
                Throw New NullReferenceException("The target type is null.")
            Case TypeCode.Int16
                Return Me.ToInt16(provider)
            Case TypeCode.Int32
                Return Me.ToInt32(provider)
            Case TypeCode.Int64
                Return Me.ToInt64(provider)
            Case TypeCode.Object
                ' Leave conversion of non-base types to derived classes.
                Throw New InvalidCastException(String.Format("Cannot convert from Temperature to {0}.", _
                                               conversionType.Name))
            Case TypeCode.SByte
                Return Me.ToSByte(provider)
            Case TypeCode.Single
                Return Me.ToSingle(provider)
            Case TypeCode.String
                Return Me.ToString(provider)
            Case TypeCode.UInt16
                Return Me.ToUInt16(provider)
            Case TypeCode.UInt32
                Return Me.ToUInt32(provider)
            Case TypeCode.UInt64
                Return Me.ToUInt64(provider)
            Case Else
                Throw New InvalidCastException("Conversion not supported.")
        End Select
    End Function

    Public Function ToUInt16(provider As IFormatProvider) As UInt16 Implements IConvertible.ToUInt16
        If temp < UInt16.MinValue Or temp > UInt16.MaxValue Then
            Throw New OverflowException(String.Format("{0} is out of range of the UInt16 data type.", temp))
        End If
        Return CUShort(Math.Round(temp))
    End Function

    Public Function ToUInt32(provider As IFormatProvider) As UInt32 Implements IConvertible.ToUInt32
        If temp < UInt32.MinValue Or temp > UInt32.MaxValue Then
            Throw New OverflowException(String.Format("{0} is out of range of the UInt32 data type.", temp))
        End If
        Return CUInt(Math.Round(temp))
    End Function

    Public Function ToUInt64(provider As IFormatProvider) As UInt64 Implements IConvertible.ToUInt64
        If temp < UInt64.MinValue Or temp > UInt64.MaxValue Then
            Throw New OverflowException(String.Format("{0} is out of range of the UInt64 data type.", temp))
        End If
        Return CULng(Math.Round(temp))
    End Function
End Class

Public Class TemperatureCelsius : Inherits Temperature : Implements IConvertible
    Public Sub New(value As Decimal)
        MyBase.New(value)
    End Sub

    ' Override ToString methods.
    Public Overrides Function ToString() As String
        Return Me.ToString(Nothing)
    End Function

    Public Overrides Function ToString(provider As IFormatProvider) As String
        Return temp.ToString(provider) + "°C"
    End Function

    ' If conversionType is a implemented by another IConvertible method, call it.
    Public Overrides Function ToType(conversionType As Type, provider As IFormatProvider) As Object
        ' For non-objects, call base method.
        If Type.GetTypeCode(conversionType) <> TypeCode.Object Then
            Return MyBase.ToType(conversionType, provider)
        Else
            If conversionType.Equals(GetType(TemperatureCelsius)) Then
                Return Me
            ElseIf conversionType.Equals(GetType(TemperatureFahrenheit))
                Return New TemperatureFahrenheit(CDec(Me.temp * 9 / 5 + 32))
                ' Unspecified object type: throw an InvalidCastException.
            Else
                Throw New InvalidCastException(String.Format("Cannot convert from Temperature to {0}.", _
                                               conversionType.Name))
            End If
        End If
    End Function
End Class

Public Class TemperatureFahrenheit : Inherits Temperature : Implements IConvertible
    Public Sub New(value As Decimal)
        MyBase.New(value)
    End Sub

    ' Override ToString methods.
    Public Overrides Function ToString() As String
        Return Me.ToString(Nothing)
    End Function

    Public Overrides Function ToString(provider As IFormatProvider) As String
        Return temp.ToString(provider) + "°F"
    End Function

    Public Overrides Function ToType(conversionType As Type, provider As IFormatProvider) As Object
        ' For non-objects, call base method.
        If Type.GetTypeCode(conversionType) <> TypeCode.Object Then
            Return MyBase.ToType(conversionType, provider)
        Else
            ' Handle conversion between derived classes.
            If conversionType.Equals(GetType(TemperatureFahrenheit)) Then
                Return Me
            ElseIf conversionType.Equals(GetType(TemperatureCelsius))
                Return New TemperatureCelsius(CDec((MyBase.temp - 32) * 5 / 9))
                ' Unspecified object type: throw an InvalidCastException.
            Else
                Throw New InvalidCastException(String.Format("Cannot convert from Temperature to {0}.", _
                                               conversionType.Name))
            End If
        End If
    End Function
End Class

下列範例說明這些 IConvertible 實作的數個呼叫,將 TemperatureCelsius 對象轉換成 TemperatureFahrenheit 物件,反之亦然。

TemperatureCelsius tempC1 = new TemperatureCelsius(0);
TemperatureFahrenheit tempF1 = (TemperatureFahrenheit)Convert.ChangeType(tempC1, typeof(TemperatureFahrenheit), null);
Console.WriteLine($"{tempC1} equals {tempF1}.");
TemperatureCelsius tempC2 = (TemperatureCelsius)Convert.ChangeType(tempC1, typeof(TemperatureCelsius), null);
Console.WriteLine($"{tempC1} equals {tempC2}.");
TemperatureFahrenheit tempF2 = new TemperatureFahrenheit(212);
TemperatureCelsius tempC3 = (TemperatureCelsius)Convert.ChangeType(tempF2, typeof(TemperatureCelsius), null);
Console.WriteLine($"{tempF2} equals {tempC3}.");
TemperatureFahrenheit tempF3 = (TemperatureFahrenheit)Convert.ChangeType(tempF2, typeof(TemperatureFahrenheit), null);
Console.WriteLine($"{tempF2} equals {tempF3}.");
// The example displays the following output:
//       0°C equals 32°F.
//       0°C equals 0°C.
//       212°F equals 100°C.
//       212°F equals 212°F.
Dim tempC1 As New TemperatureCelsius(0)
Dim tempF1 As TemperatureFahrenheit = CType(Convert.ChangeType(tempC1, GetType(TemperatureFahrenheit), Nothing), TemperatureFahrenheit)
Console.WriteLine("{0} equals {1}.", tempC1, tempF1)
Dim tempC2 As TemperatureCelsius = CType(Convert.ChangeType(tempC1, GetType(TemperatureCelsius), Nothing), TemperatureCelsius)
Console.WriteLine("{0} equals {1}.", tempC1, tempC2)
Dim tempF2 As New TemperatureFahrenheit(212)
Dim tempC3 As TEmperatureCelsius = CType(Convert.ChangeType(tempF2, GEtType(TemperatureCelsius), Nothing), TemperatureCelsius)
Console.WriteLine("{0} equals {1}.", tempF2, tempC3)
Dim tempF3 As TemperatureFahrenheit = CType(Convert.ChangeType(tempF2, GetType(TemperatureFahrenheit), Nothing), TemperatureFahrenheit)
Console.WriteLine("{0} equals {1}.", tempF2, tempF3)
' The example displays the following output:
'       0°C equals 32°F.
'       0°C equals 0°C.
'       212°F equals 100°C.
'       212°F equals 212°F.

TypeConverter 類別

.NET 也可讓您藉由擴充 System.ComponentModel.TypeConverter 類別,以及透過 System.ComponentModel.TypeConverterAttribute 屬性將型別轉換器與型別產生關聯,來定義自定義類型的型別轉換器。 下表顯示出此方法與對自定義類型實作 IConvertible 介面的差異。

備註

只有在自定義類型已定義類型轉換器時,才能提供設計時間支援。

使用 TypeConverter 進行轉換 使用 IConvertible 轉換
藉由從 TypeConverter衍生個別類別,以實作自定義型別。 套用 TypeConverterAttribute 屬性,這個衍生類別會與自定義類型相關聯。 是由自定義類型實作以執行轉換。 型別的用戶會在該型別上調用 IConvertible 轉換方法。
可以在設計階段與執行階段使用。 只能在運行時間使用。
使用反射,因此,速度會比由 IConvertible啟用的轉換慢。 不使用反射。
允許將雙向類型從自定義類型轉換成其他數據類型,以及從其他數據類型轉換成自定義類型。 例如,針對 MyType 定義的 TypeConverter 允許從 MyType 轉換為 String,以及從 String 轉換為 MyType 允許從自定義類型轉換成其他數據類型,但不允許從其他數據類型轉換成自定義類型。

如需使用類型轉換器執行轉換的詳細資訊,請參閱 System.ComponentModel.TypeConverter

另請參閱