Typkonvertering i .NET
Varje värde har en associerad typ, som definierar attribut, till exempel mängden utrymme som allokeras till värdet, det intervall med möjliga värden som det kan ha och de medlemmar som det gör tillgängligt. Många värden kan uttryckas som fler än en typ. Till exempel kan värdet 4 uttryckas som ett heltal eller ett flyttalsvärde. Typkonvertering skapar ett värde i en ny typ som motsvarar värdet för en gammal typ, men som inte nödvändigtvis bevarar identiteten (eller det exakta värdet) för det ursprungliga objektet.
.NET stöder automatiskt följande konverteringar:
Konvertering från en härledd klass till en basklass. Det innebär till exempel att en instans av en klass eller struktur kan konverteras till en Object instans. Den här konverteringen kräver ingen gjutnings- eller konverteringsoperator.
Konvertering från en basklass tillbaka till den ursprungliga härledda klassen. I C# kräver den här konverteringen en gjutningsoperator. I Visual Basic krävs operatorn om
Option Strict
denCType
är aktiverad.Konvertering från en typ som implementerar ett gränssnitt till ett gränssnittsobjekt som representerar gränssnittet. Den här konverteringen kräver ingen gjutnings- eller konverteringsoperator.
Konvertering från ett gränssnittsobjekt tillbaka till den ursprungliga typen som implementerar gränssnittet. I C# kräver den här konverteringen en gjutningsoperator. I Visual Basic krävs operatorn om
Option Strict
denCType
är aktiverad.
Utöver dessa automatiska konverteringar tillhandahåller .NET flera funktioner som stöder konvertering av anpassad typ. Dessa omfattar följande:
Operatorn
Implicit
, som definierar tillgängliga breddningskonverteringar mellan typer. Mer information finns i avsnittet Implicit konvertering med implicit operator .Operatorn
Explicit
, som definierar tillgängliga begränsade konverteringar mellan typer. Mer information finns i avsnittet Explicit konvertering med explicit operator .Gränssnittet IConvertible , som definierar konverteringar till var och en av de grundläggande .NET-datatyperna. Mer information finns i avsnittet IConvertible Interface( IConvertible Interface ).
Klassen Convert , som innehåller en uppsättning metoder som implementerar metoderna i IConvertible gränssnittet. Mer information finns i avsnittet Konvertera klass .
Klassen TypeConverter , som är en basklass som kan utökas för att stödja konvertering av en angiven typ till någon annan typ. Mer information finns i avsnittet TypeConverter-klass .
Implicit konvertering med implicit operator
Vidgade konverteringar innebär att ett nytt värde skapas från värdet för en befintlig typ som antingen har ett mer restriktivt intervall eller en mer begränsad medlemslista än måltypen. Vidgade konverteringar kan inte leda till dataförlust (även om de kan leda till förlust av precision). Eftersom data inte kan gå förlorade kan kompilatorer hantera konverteringen implicit eller transparent, utan att kräva användning av en explicit konverteringsmetod eller en gjutningsoperator.
Kommentar
Även om kod som utför en implicit konvertering kan anropa en konverteringsmetod eller använda en gjutningsoperator, krävs inte deras användning av kompilatorer som stöder implicita konverteringar.
Typen stöder till exempel Decimal implicita konverteringar från Bytevärdena , Char, Int16, Int32, Int64, SByte, UInt16, UInt32och UInt64 . I följande exempel visas några av dessa implicita konverteringar när du tilldelar värden till en Decimal variabel.
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.
Om en viss språkkompilator stöder anpassade operatorer kan du också definiera implicita konverteringar i dina egna anpassade typer. I följande exempel visas en partiell implementering av en signerad bytedatatyp med namnet ByteWithSign
som använder tecken- och storleksrepresentation. Den stöder implicit konvertering av Byte värden och SByte värden till ByteWithSign
värden.
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
Klientkoden kan sedan deklarera en ByteWithSign
variabel och tilldela den Byte och SByte värden utan att utföra några explicita konverteringar eller använda några gjutningsoperatorer, som i följande exempel visas.
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
Explicit konvertering med den explicita operatorn
För att begränsa konverteringar kan du skapa ett nytt värde från värdet för en befintlig typ som antingen har ett större intervall eller en större medlemslista än måltypen. Eftersom en begränsad konvertering kan leda till dataförlust kräver kompilatorer ofta att konverteringen görs explicit via ett anrop till en konverteringsmetod eller en gjutningsoperator. Konverteringen måste alltså hanteras explicit i utvecklarkoden.
Kommentar
Det huvudsakliga syftet med att kräva en konverteringsmetod eller gjutningsoperator för att begränsa konverteringar är att göra utvecklaren medveten om risken för dataförlust eller en OverflowException så att den kan hanteras i kod. Vissa kompilatorer kan dock lätta på det här kravet. I Visual Basic försöker kompilatorn för Visual Basic till exempel utföra begränsade konverteringar implicit om Option Strict
den är av (standardinställningen).
Datatyperna UInt32, Int64och UInt64 har till exempel intervall som överskrider datatypen Int32 , vilket visas i följande tabell.
Typ | Jämförelse med int32-intervall |
---|---|
Int64 | Int64.MaxValue är större än Int32.MaxValue, och Int64.MinValue är mindre än (har ett större negativt intervall än) Int32.MinValue. |
UInt32 | UInt32.MaxValue är större än Int32.MaxValue. |
UInt64 | UInt64.MaxValue är större än Int32.MaxValue. |
För att hantera sådana begränsade konverteringar tillåter .NET typer att definiera en Explicit
operator. Enskilda språkkompilatorer kan sedan implementera den här operatorn med hjälp av sin egen syntax, eller så kan en medlem i Convert klassen anropas för att utföra konverteringen. (Mer information om Convert klassen finns i Konvertera klass senare i det här avsnittet.) I följande exempel visas användningen av språkfunktioner för att hantera den explicita konverteringen av dessa potentiellt out-of-range heltalsvärden till Int32 värden.
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: {0} exceeds {1}.",
number1, int.MaxValue);
else
Console.WriteLine("Conversion failed: {0} is less than {1}.",
number1, 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: {0} exceeds {1}.",
number2, 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: {0} exceeds {1}.",
number1, 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.
Explicita konverteringar kan ge olika resultat på olika språk, och dessa resultat kan skilja sig från det värde som returneras av motsvarande Convert metod. Om till exempel Double värdet 12.63251 konverteras till en Int32, trunkerar både Visual Basic-metoden CInt
och .NET-metoden Convert.ToInt32(Double)Double för att returnera värdet 13, men C# (int)
-operatorn trunkerar Double för att returnera värdet 12. På samma sätt stöder C# (int)
-operatorn inte boolesk-till-heltalskonvertering, men Visual Basic-metoden CInt
konverterar värdet true
-1. Å Convert.ToInt32(Boolean) andra sidan konverterar metoden värdet true
1.
De flesta kompilatorer tillåter att explicita konverteringar utförs på ett kontrollerat eller avmarkerat sätt. När en kontrollerad konvertering utförs utlöses ett OverflowException när värdet för den typ som ska konverteras ligger utanför måltypens intervall. När en omarkerad konvertering utförs under samma villkor kanske konverteringen inte utlöser ett undantag, men det exakta beteendet blir odefinierat och ett felaktigt värde kan resultera.
Kommentar
I C# kan kontrollerade konverteringar utföras med hjälp av nyckelordet checked
tillsammans med en gjutningsoperator eller genom att ange kompileringsalternativet /checked+
. Omvänt kan omarkerade konverteringar utföras med hjälp av nyckelordet unchecked
tillsammans med gjutningsoperatorn eller genom att ange kompileringsalternativet /checked-
. Som standard avmarkeras explicita konverteringar. I Visual Basic kan markerade konverteringar utföras genom att avmarkera kryssrutan Ta bort heltalsspill i projektets dialogrutan Avancerad kompilator Inställningar eller genom att ange kompilatoralternativet/removeintchecks-
. Däremot kan omarkerade konverteringar utföras genom att markera kryssrutan Ta bort heltalsspill i projektets dialogrutan Avancerad kompilator Inställningar eller genom att ange kompileringsalternativet/removeintchecks+
. Som standard kontrolleras explicita konverteringar.
I följande C#-exempel används nyckelorden checked
och unchecked
för att illustrera skillnaden i beteende när ett värde utanför intervallet för en Byte konverteras till en Byte. Den markerade konverteringen utlöser ett undantag, men den omarkerade konverteringen tilldelar Byte.MaxValue variabeln 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("{0} is outside the range of the Byte data type.",
largeValue);
}
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("{0} is outside the range of the Byte data type.",
largeValue);
}
// 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.
Om en viss språkkompilator stöder anpassade överlagrade operatorer kan du också definiera explicita konverteringar i dina egna anpassade typer. I följande exempel visas en partiell implementering av en signerad bytedatatyp med namnet ByteWithSign
som använder tecken- och storleksrepresentation. Den stöder explicit konvertering av Int32 värden och UInt32 värden till ByteWithSign
värden.
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
Klientkoden kan sedan deklarera en ByteWithSign
variabel och tilldela den Int32 och UInt32 värden om tilldelningarna innehåller en gjutningsoperator eller en konverteringsmetod, vilket visas i följande exempel.
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-gränssnittet
För att stödja konvertering av alla typer till en vanlig språkkörningsbastyp tillhandahåller IConvertible .NET gränssnittet. Implementeringstypen krävs för att tillhandahålla följande:
En metod som returnerar TypeCode implementeringstypen.
Metoder för att konvertera implementeringstypen till varje vanlig språkkörningsbastyp (Boolean, , ByteDateTime, Decimal, Doubleoch så vidare).
En generaliserad konverteringsmetod för att konvertera en instans av implementeringstypen till en annan angiven typ. Konverteringar som inte stöds bör utlösa en InvalidCastException.
Varje vanlig språkkörningsbastyp (dvs. Boolean, Byte, Char, DateTime, Decimal, Double, Int16, Int32, Int64, , SByte, Single, String, UInt16, UInt32, och UInt64), samt typerna DBNull och Enum implementerar IConvertible gränssnittet. Dessa är dock explicita gränssnittsimplementeringar. konverteringsmetoden kan bara anropas via en IConvertible gränssnittsvariabel, vilket visas i följande exempel. I det här exemplet konverteras ett Int32 värde till motsvarande Char värde.
int codePoint = 1067;
IConvertible iConv = codePoint;
char ch = iConv.ToChar(null);
Console.WriteLine("Converted {0} to {1}.", codePoint, 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)
Kravet på att anropa konverteringsmetoden i dess gränssnitt i stället för på implementeringstypen gör explicita gränssnittsimplementeringar relativt dyra. I stället rekommenderar vi att du anropar lämplig medlem i Convert klassen för att konvertera mellan vanliga språkkörningsbastyper. Mer information finns i nästa avsnitt, Konvertera klass.
Kommentar
Förutom gränssnittet IConvertible och klassen Convert som tillhandahålls av .NET kan enskilda språk också ge sätt att utföra konverteringar. C# använder till exempel gjutningsoperatorer. Visual Basic använder kompilator-implementerade konverteringsfunktioner som CType
, CInt
och DirectCast
.
För det mesta IConvertible är gränssnittet utformat för att stödja konvertering mellan bastyperna i .NET. Gränssnittet kan dock också implementeras av en anpassad typ för att stödja konvertering av den typen till andra anpassade typer. Mer information finns i avsnittet Anpassade konverteringar med Metoden ChangeType senare i det här avsnittet.
Klassen Convert
Även om varje bastyps gränssnittsimplementering kan anropas IConvertible för att utföra en typkonvertering, är anrop av System.Convert klassmetoderna det rekommenderade språkneutrala sättet att konvertera från en bastyp till en annan. Dessutom Convert.ChangeType(Object, Type, IFormatProvider) kan metoden användas för att konvertera från en angiven anpassad typ till en annan typ.
Konverteringar mellan bastyper
Klassen Convert ger ett språkneutralt sätt att utföra konverteringar mellan bastyper och är tillgängligt för alla språk som är inriktade på den gemensamma språkkörningen. Den innehåller en fullständig uppsättning metoder för både breddning och minskning av konverteringar och genererar en InvalidCastException för konverteringar som inte stöds (till exempel konvertering av ett DateTime värde till ett heltalsvärde). Begränsade konverteringar utförs i en kontrollerad kontext och en OverflowException utlöses om konverteringen misslyckas.
Viktigt!
Convert Eftersom klassen innehåller metoder för att konvertera till och från varje bastyp eliminerar den behovet av att anropa varje bastyps explicita gränssnittsimplementeringIConvertible.
I följande exempel visas hur klassen används System.Convert för att utföra flera bredare och smalare konverteringar mellan .NET-bastyper.
// 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.
I vissa fall, särskilt när du konverterar till och från flyttalsvärden, kan en konvertering innebära en förlust av precision, även om den inte utlöser en OverflowException. I följande exempel visas den här förlusten av precision. I det första fallet har ett Decimal värde mindre precision (färre signifikanta siffror) när det konverteras till en Double. I det andra fallet avrundas ett Double värde från 42,72 till 43 för att slutföra konverteringen.
double doubleValue;
// Convert a Double to a Decimal.
decimal decimalValue = 13956810.96702888123451471211m;
doubleValue = Convert.ToDouble(decimalValue);
Console.WriteLine("{0} converted to {1}.", decimalValue, doubleValue);
doubleValue = 42.72;
try
{
int integerValue = Convert.ToInt32(doubleValue);
Console.WriteLine("{0} converted to {1}.",
doubleValue, integerValue);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to an integer.",
doubleValue);
}
// 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.
En tabell som visar både bredare och smala konverteringar som stöds av klassen finns i Convert Typkonverteringstabeller.
Anpassade konverteringar med metoden ChangeType
Förutom att stödja konverteringar till var och en av bastyperna Convert kan klassen användas för att konvertera en anpassad typ till en eller flera fördefinierade typer. Den här konverteringen Convert.ChangeType(Object, Type, IFormatProvider) utförs av metoden, som i sin tur omsluter ett anrop till IConvertible.ToType parameterns value
metod. Det innebär att objektet som representeras av parametern value
måste tillhandahålla en implementering av IConvertible gränssnittet.
Kommentar
Convert.ChangeType(Object, Type) Eftersom metoderna och Convert.ChangeType(Object, Type, IFormatProvider) använder ett Type objekt för att ange den måltyp som value
konverteras till, kan de användas för att utföra en dynamisk konvertering till ett objekt vars typ inte är känd vid kompileringstillfället. Observera dock att implementeringen IConvertible av value
fortfarande måste stödja den här konverteringen.
I följande exempel visas en möjlig implementering av IConvertible gränssnittet som gör att ett TemperatureCelsius
objekt kan konverteras till ett TemperatureFahrenheit
objekt och vice versa. Exemplet definierar en basklass, Temperature
, som implementerar IConvertible gränssnittet och åsidosätter Object.ToString metoden. Härledda TemperatureCelsius
klasser och TemperatureFahrenheit
klasser åsidosätter ToType
var och en ToString
metoderna för basklassen.
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
I följande exempel visas flera anrop till dessa IConvertible implementeringar för att konvertera TemperatureCelsius
objekt till TemperatureFahrenheit
objekt och vice versa.
TemperatureCelsius tempC1 = new TemperatureCelsius(0);
TemperatureFahrenheit tempF1 = (TemperatureFahrenheit)Convert.ChangeType(tempC1, typeof(TemperatureFahrenheit), null);
Console.WriteLine("{0} equals {1}.", tempC1, tempF1);
TemperatureCelsius tempC2 = (TemperatureCelsius)Convert.ChangeType(tempC1, typeof(TemperatureCelsius), null);
Console.WriteLine("{0} equals {1}.", tempC1, tempC2);
TemperatureFahrenheit tempF2 = new TemperatureFahrenheit(212);
TemperatureCelsius tempC3 = (TemperatureCelsius)Convert.ChangeType(tempF2, typeof(TemperatureCelsius), null);
Console.WriteLine("{0} equals {1}.", tempF2, tempC3);
TemperatureFahrenheit tempF3 = (TemperatureFahrenheit)Convert.ChangeType(tempF2, typeof(TemperatureFahrenheit), null);
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.
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.
Klassen TypeConverter
Med .NET kan du också definiera en typkonverterare för en anpassad typ genom att System.ComponentModel.TypeConverter utöka klassen och associera typkonverteraren med typen via ett System.ComponentModel.TypeConverterAttribute attribut. I följande tabell visas skillnaderna mellan den här metoden och implementeringen av IConvertible gränssnittet för en anpassad typ.
Kommentar
Designtidsstöd kan endast tillhandahållas för en anpassad typ om en typkonverterare har definierats för den.
Konvertering med TypeConverter | Konvertering med IConvertible |
---|---|
Implementeras för en anpassad typ genom att härleda en separat klass från TypeConverter. Den här härledda klassen är associerad med den anpassade typen genom att använda ett TypeConverterAttribute attribut. | Implementeras av en anpassad typ för att utföra konvertering. En användare av typen anropar en IConvertible konverteringsmetod för typen. |
Kan användas både vid designtid och vid körning. | Kan endast användas vid körning. |
Använder reflektion; är därför långsammare än konverteringen som aktiveras av IConvertible. | Använder inte reflektion. |
Tillåter tvåvägskonverteringar från den anpassade typen till andra datatyper och från andra datatyper till den anpassade typen. En definierad för tillåter till exempel TypeConverter konverteringar från MyType till Stringoch från String till MyType .MyType |
Tillåter konvertering från en anpassad typ till andra datatyper, men inte från andra datatyper till den anpassade typen. |
Mer information om hur du använder typkonverterare för att utföra konverteringar finns i System.ComponentModel.TypeConverter.