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異常。
不論隱式還是顯式引用轉換,雖然可能會改變引用值的類型,卻不會改變值本身。