C#的隱式型別轉換和顯式型別轉換
C#的隱式型別轉換和顯式型別轉換
C#的隱式型別轉換
在C#語言中,一些預定義的資料型別之間存在著預定義的轉換。比如,從int型別轉換到long型別。C#語言中資料型別的轉換可以用分為兩
類:隱式轉換(implicit conversions)和顯式轉換(explicit conversions)。這篇文章我們將詳細介紹這兩類轉換。
1. 隱式型別轉換
隱式轉換就是系統預設的、不需要加以宣告就可以進行的轉換。在隱式轉換過程中,編譯器無需對轉換進行詳細檢查就能夠安全地執行轉換。
比如從int型別轉換到long型別就是一種隱式轉換。隱式轉換一般不會失敗,轉換過程中也不會導致資訊丟失。
比如:
int i=10;
long l=i;
裝箱轉換實際上就是一種隱式型別轉換。在本節,我們還將講解以下隱式轉換的規則:
1.1 隱式數值轉換
隱式數值轉換包括以下幾種:
●從sbyte型別到short,int,long,float,double,或decimal型別。
●從byte型別到short,ushort,int,uint,long,ulong,float,double,或decimal型別。
●從short型別到int,long,float,double,或decimal型別。
●從ushort型別到int,uint,long,ulong,float,double,或decimal型別。
●從int型別到long,float,double,或decimal型別。
●從uint型別到long,ulong,float,double,或decimal型別。
●從long型別到float,double,或decimal型別。
●從ulong型別到float,double,或decimal型別。
●從char型別到ushort,int,uint,long,ulong,float,double,或decimal型別。
●從float型別到double型別。
其中,從int,uint,或long到float以及從long到double的轉換可能會導致精度下降,但決不會引起數量上的丟失。其它的隱式數值轉換則不會
有任何資訊丟失。
結合我們在資料型別中學習到的值型別的範圍,我們可以發現,隱式數值轉換實際上就是從低精度的數值型別到高精度的數值型別的轉換。
從上面的10條我們可以看出,不存在到char型別的隱式轉換,這意味著其它整型值不能自動轉換為char型別。這一點我們需要特別注意。
下面的程式給出了隱式數值轉換的例子。
程式清單1-1:
using System;
class Test
{
public static void Main()
{
byte x=16;
Console.WriteLine("x={0}",x);
ushort y=x;
Console.WriteLine("y={0}",y);
y=65535;
Console.WriteLine("y={0}",y);
float z=y;
Console.WriteLine("z={0}",z);
}
}程式的輸出將是:
x=16;
y=16;
y=65535;
z=65535;
如果我們在上面程式中的語句之後再加上一句:
y=y+1;
再重新編譯程式時,編譯器將會給出一條錯誤資訊:
can not implictly convert type 'int' to type 'ushort'
這說明,從整數型別65536到無符號短整型y不存在隱式轉換。
1.2 隱式列舉轉換
隱式列舉轉換允許把十進位制整數0轉換成任何列舉型別,對應其它的整數則不存在這種隱式轉換。還是讓我們用例子來說明。
程式清單1-2:
using System;
enum Weekday{
Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday
};
class Test
{
public static void Main(){
Weekday day;
day=0;
Console.WriteLine(day);
}
}程式的輸出是:
0
但是如果我們把語句day=0改寫為day=1,編譯器就會給出錯誤:
Can not implictly convert type 'int' type 'enum'
1.3 隱式引用轉換
隱式引用轉換包括以下幾類:
●從任何引用型別到物件型別的轉換
●從類型別s到類型別t的轉換,其中s是t的派生類。
●從類型別s到介面型別t的轉換,其中類s實現了介面t。
●從介面型別s到介面型別t的轉換,其中t是s的父介面。
從元素型別為Ts的陣列型別S向元素型別為Tt的陣列型別T轉換,這種轉換需要滿足下列條件:
●S和T只有元素的資料型別不同,但它們的維數相同。
●Ts和Tt都是引用型別。
●存在從Ts到Tt的隱式引用轉換
●從任何陣列型別到System.Array的轉換。
●從任何代表型別到System.Delegate的轉換。
●從任何資料型別或代表型別到System.ICLoneable的轉換。
●從空型別(null)到任何引用型別的轉換。
比如,下面的程式無法通過編譯,因為陣列的元素型別是值型別,C#中不存在這樣的隱式轉換。
程式清單1-3:
using System;
class Test
{
public static void Main(){
float[] float_arr=new float[10];
int[] int_arr=new int[10];
float_arr=int_arr;
}
}
而下面這段程式則是正確的:
程式清單1-4:
using System;
class Class1
{}
class Class2:Class1
{}
class Test
{
public static void Main(){
Class1[] class1_arr=new Class1[10];
class2[] class2_arr=new Class2[10];
class1_arr=class2_arr;
}
}
程式1-5很有趣,它給出了我們常用的值型別在系統環境中的原型定義。
程式1-5:
using System;
class Test
{
public static void Main(){
float[] float_arr=new float[10];
double[] double_arr=new double[10];
sbyte[] sbyte_arr=new sbyte[10];
byte[] byte_arr=new byte[10];
ushort[] ushort_arr=new ushort[10];
int[] int_arr=new int[10];
long[] long_arr=new long[10];
string[] string_arr=new string[10];
console.WriteLine(float_arr);
Console.WriteLine(double_arr);
Console.WriteLine(sbyte_arr);
Console.WriteLine(byte_arr);
Console.WriteLine(ushort_arr);
Console.WriteLine(int_arr);
Console.WriteLine(long_arr);
Console.WriteLine(string_arr);
}
}
程式的輸出結果是:
System.Single[];
System.Double[];
System.Sbyte[];
System.Byte[];
System.Int16[];
system.Int32[];
System.Int64[];
System.String[];
2.C#的顯式型別轉換
顯式型別轉換,又叫強制型別轉換。與隱式轉換正好相反,顯式轉換需要使用者明確地指定轉換的型別。比如下面的例子把一個型別顯式轉換為
型別:
long l=5000;
int i=(int)l;
拆箱轉換就是一種顯式轉換。這裡我們還將講解以下轉換的規則:
●顯式數值轉換
●顯式列舉轉換
●顯式引用轉換
顯式轉換可以發生在表示式的計算過程中。它並不是總能成功,而且常常可能引起資訊丟失。
顯式轉換包括所有的隱式轉換,也就是說把任何系統允許的隱式轉換寫成顯式轉換的形式都是允許的,如:
int i=10;
long l=(long)i;
2.1 顯式數值轉換
顯式數值轉換是指當不存在相應的隱式轉換時,從一種數字型別到另一種數字型別的轉換。包括:
●從sbyte到byte,ushort,uint,ulong,或char。
●從byte到sbyte或char。
●從short到sbyte,byte,ushort,uint,ulong,或char。
●從ushort到sbyte,byte,short,或char。
●從int到sbyte,byte,short,ushort,uint,ulong,或char。
●從uint到sbyte,byte,short,ushort,int,或char。
●從long到sbyte,byte,short,ushort,int,uint,ulong,或char。
●從ulong到sbyte,byte,short,ushort,int,uint,long,或char。
●從char到sbyte,byte,或short。
●從float到sbyte,byte,short,ushort,int,uint,long,ulong,char,或decimal。
●從double到sbyte,byte,short,ushort,int,uint,long,ulong,char,float,或decimal。
●從decimal到sbyte,byte,short,ushort,int,uint,long,ulong,char,float,或double。
這種型別轉換有可能丟失資訊或導致異常丟擲,轉換按照下列規則進行:
●對於從一種整型到另一種整型的轉換,編譯器將針對轉換進行溢位檢測,如果沒有發生溢位,轉換成功,否則丟擲一個OverflowException異
常。這種檢測還與編譯器中是否設定了checked選項有關。
●對於從float,double,或decimal到整型的轉換,源變數的值通過舍入到最接近的整型值作為轉換的結果。如果這個整型值超出了目標型別的
值域,則將丟擲一個OverflowException異常。
●對於從double到float的轉換,double值通過舍入取最接近的float值。如果這個值太小,結果將變成正0或負0;如果這個值太大,將變成正
無窮或負無窮。如果原double值是Nan,則轉換結果也是NaN。
●對於從float或double到decimal的轉換,源值將轉換成小數形式並通過舍入取到小數點後28位(如果有必要的話)。如果源值太小,則結果
為0;如果太大以致不能用小數表示,或是無窮和NaN,則將丟擲InvalidCastException異常。
●對於從decimal到float或double的轉換,小數的值通過舍入取最接近的值。這種轉換可能會丟失精度,但不會引起異常。
程式清單2-1:
using system;
class Test
{
static void Main(){
long longValue=Int64.MaxValue;
int intValue=(int)longValue;
Console.WriteLine("(int){0}={1}",longValue,intValue);
}
}
這個例子把一個int型別轉換成為long型別,輸出結果是:
(int)9223372036854775807=-1
這是因為發生了溢位,從而在顯式型別轉換時導致了資訊丟失。
2.2 顯式列舉轉換
顯式列舉轉換包括以下內容:
●從sbye,byte,short,ushort,int,uint,long,ulong,char,float,double,或decimal到任何列舉型別。
●從任何列舉型別到sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,或decimal。
●從任何列舉型別到任何其它列舉型別。
顯式列舉轉換是這樣進行的:它實際上是列舉型別的元素型別與相應型別之間的隱式或顯式轉換。比如,有一個元素型別為int的列舉型別E,
則當執行從E到byte的顯式列舉轉換時,實際上作的是從int到byte的顯式數字轉換;當執行從byte到E的顯式列舉轉換時,實際上是執行byte到
int的隱式數字轉換。
比如,對程式1-2,我們改寫如下:
程式清單2-2:
using System;
enum Weekday{
Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday
};
class Test
{
public static void Main(){
Weekday day;
day=(Weekday)3;
Console.WriteLine(day);
}
}
程式的輸出是:
3
2.3 顯式引用轉換
顯式引用轉換包括:
●從物件到任何引用型別
●從類型別S到類型別T,其中S是T的基類。
●從基型別S到介面型別T,其中S不是密封類,而且沒有實現T。
●從介面型別S到類型別T,其中T不是密封類,而且沒有實現S。
●從介面型別S到介面型別T,其中S不是T的子介面。
從元素型別為Ts的陣列型別S到元素型別為Tt的陣列型別T的轉換,這種轉換需要滿足下列條件:
●S和T只有元素的資料型別不同,而維數相同。
●Ts和Tt都是引用型別。
●存在從Ts到Tt的顯式引用轉換。
●從System.Array到陣列型別。
●從System.Delegate到代表型別。
●從System.ICloneable到陣列型別或代表型別。
顯式引用轉換髮生在引用型別之間,需要在執行時檢測以確保正確。
為了確保顯式引用轉換的正常執行,要求源變數的值必須是null或者它所引用的物件的型別可以被隱式引用轉換為目標型別。否則顯式引用轉
換失敗,將丟擲一個InvalidCastException異常。
不論隱式還是顯式引用轉換,雖然可能會改變引用值的型別,卻不會改變值本身。