一. 引言
型別安全是.NET設計之初重點考慮的內容之一,對於程式設計者來說,完全把握系統資料的型別安全,經常是力不從心的問題。現在,這一切已經在微軟大牛們的設計架構中為你解決了。可以有以下方法在運行時擷取對象的類型資訊:
1.1typeof 運算子。如 System.Type type = typeof(int?);
1.2使用 System.Reflection 命名空間的類和方法。
1.3GetType()方法。在.NET中,一切類型都必須繼承自System.Object類型,System.Object類裡有一個GetType()方法。但是,此方法不能應用於可空實值型別(可空實值型別可以表示基礎類型的所有值,另外還可以表示 null 值)。如果您試圖使用 GetType 方法或 is 運算子在運行時獲得可空類型變數的類型資訊,得到的結果是表示可空實值型別的基礎類型而不是可空類型本身的 Type 對象。
1.4as 和 is 運算子。根據MSDN的說明:由於對象是多態的,因此基類類型的變數可以儲存衍生類別型。若要訪問衍生類別型的方法,需要將值強制轉換回該衍生類別型。不過,在這些情況下,如果只嘗試進行簡單的強制轉換,會導致引發 InvalidCastException 的風險。鑒於這個過程是不安全的,因此需要用 try - catch 語句塊來進行保護,例如比較安全的代碼方式應該如下所示:
// 有一object類型的待轉換對象 objTest
GivenType value = null;
try
{
value = (GivenType) objTest;
}
catch( Exception e )
{
MessageBox.Show( e.Message );
}
但是如上的寫法在C#中已經屬於過時的寫法,而且也屬於比較低效的寫法。但是類似的轉換會經常發生,為了避免異常導致的低效和代碼的不簡潔,C# 提供 is 和 as 運算子來進行轉換。可以使用這兩個運算子來測試強制轉換是否會成功,而沒有引發異常的風險。
二、is 運算子
is 運算子檢查對象是否與給定類型相容。例如,if ( obj is MyObject ) 將檢查對象 obj 是否為 MyObject 類型的一個執行個體,或者是從 MyObject 派生的一個類型的執行個體。
對於is運算式的結果應該這麼看待:如果所提供的對象(運算式)非空,並且可以被強制轉換為所提供的類型而不會引發異常,則is運算式的結果就為true,否則為false。
通常使用is運算式的情況是程式運行時才計算類型相容性,如果已知運算式的值始終為true或者fale,將會導致編譯時間的警告。
注意:is運算子只考慮引用轉換、裝箱轉換和拆箱轉換,不考慮其他轉換(包括使用者定義的轉換)。例如雖然int和double是類型相容的,但是使用is運算子的結果卻是false。
其他需要注意的:
不能重載is運算子;
在is運算子左側不允許使用匿名方法(Lambda運算式例外)。
三、as 運算子
as 運算子用於在相容的參考型別之間執行類似於強制類型轉換的操作。與強制類型轉換不同的是,如果無法進行轉換,as運算子將返回null而不是引發異常。
文法:
expression as type
等效於:
expression is type ? (type) expression : (type) null
注意:as 運算子只能執行引用轉換和裝箱轉換,不能執行拆箱轉換(應使用is運算子判斷配合強制類型裝換來完成),也不能執行其他轉換,如使用者定義的轉換(這類轉換應該首先需要相應類型提供轉換函式並使用強制轉換運算式來完成,或者使用case語句等其他方法來執行)。下面舉例說明:
2.1 不能使用 as 運算子進行拆箱轉換
即 as 運算子不能應用在實值型別資料,如下寫會出現編譯錯誤:
object objTest = 11;
int intValue = objTest as int;
正確的寫法是:使用is操作符,再加上顯式的類型轉換操作,就可以安全完成轉換,例如:
object objTest = 11;
if ( objTest is int )
{
int intValue = (int) objTest;
}
2.2 不能使用 as 運算子完成使用者定義的轉換
要想使使用者定義的轉換操作能正確完成,需要在原類型中增加類型轉換操作符函數,例如:
public class NewTypeOne
{
public static explicit operator NewTypeTwo( NewTypeOne objTypeOne )
{
//Convert object into new type
}
}
並使用如下強制類型轉換(不能使用 as 運算子):
NewTypeOne objTypeOne = new NewTypeOne();
NewTypeTwo newTestTwo = (NewTypeTwo)newTestOne;
2.3 對 as 運算子使用可空類型
允許使用 as 運算子完成基礎資料型別 (Elementary Data Type)(已知實值型別)之間的轉換,但是由於使用 as 運算子可能產生null值,應注意可能需要對 as 運算子使用 nullable 類型,否則同樣可能拋出異常。
下面是摘自MSDN的代碼:
void UseAsWithNullable(System.ValueType val)
{
int? j = val as int?;
if (j != null)
{
Console.WriteLine(j);
}
else
{
Console.WriteLine("Could not convert " + val.ToString());
}
}
四、as 運算子和 is 運算子的效率比較
通常,as 運算子更高效一些,因為如果可以成功進行強制轉換,它會實際返回強制轉換值。而 is 運算子只返回一個布爾值,可能在運算式為true時,還需要進行顯示的轉換,就需要執行兩次類型相容檢查,例如:
object o ="abc";
if ( o is string) //執行第一次類型相容性檢查
{
string s = (string)o; //執行第二次類型相容性檢查,並轉換
MessageBox.Show( "轉換成功!" );
}
else
{
MessageBox.Show( "轉換失敗!" );
}
而使用as運算子,可以改寫為:
object o ="abc";
string s = oasstring;//執行第一次類型相容性檢查,並返回結果
if(s != null)
MessageBox.Show("轉換成功!");
else
MessageBox.Show("轉換失敗!");
對比兩種方式,is 需要做兩次對象的類型檢查;而 as 需要做一次物件類型檢查,再加一次null的檢查,但是null檢查的開銷比物件類型檢查少,因此相對來說,as的方法效率高些。當然,只是檢測類型是否相符那麼只用is就可以了,如果要進行類型轉化可以直接用as。
五、類型轉換總結
綜上所述,那麼在進行類型轉換的時候,可以按照如下的方式進行選擇。
類型轉換
使用操作
Object --> 已知參考型別
使用 as 操作符來完成
Object --> 基礎資料型別 (Elementary Data Type)(拆箱)
先使用 is 操作符來進行判斷,再用強制轉換方式進行轉換
使用者定義型別之間轉換
首先需要相應類型提供轉換函式,再用強制轉換方式進行轉換
基礎資料型別 (Elementary Data Type)之間轉換
最好使用.Net提供的Convert類所涉及的靜態方法
其他類型轉換相關:
1. 任何類型都可以轉換為其基類類型,用隱式轉換即可完成;
2. 任何類型轉換為其衍生類別型時,必須進行顯示轉換。如:(類型名)對象名;
3. 使用 GetType 可以取得任何對象的精確類型;
4. 基礎資料型別 (Elementary Data Type)是都是已知的實值型別,可以使用 Convert 類實作類別型轉換;
5. 除了 string 以外的其他類型都有 Parse 方法,用於將字串類型轉換成對應的基本類型;
6. 實值型別和參考型別的轉換稱為裝箱(boxing)或拆箱(unboxing)。(根據MSDN編程指南:裝箱是將實值型別轉換為object類型或由此實值型別實現的任一介面類型的過程。當CLR對實值型別進行裝箱時,會將該值封裝到System.Object內部,再將後者儲存在託管堆上。unboxing將從對象中提取實值型別。)
淺談了類型轉換的幾個普遍關注的方面,該將主要精力放在is、as操作符的恩怨情仇上了。類型轉換將是個較大的話題,留於適當的時機討論。
is/as操作符,是C#中用於類型轉換的,提供了對類型相容性的判斷,從而使得類型轉換控制在安全的範疇,提供了靈活的類型轉換控制。
is的規則如下:
檢查物件類型的相容性,並返回結果,true或者false;
如果對象為null,則傳回值永遠為false。
參考:
1. 如何:使用 as 和 is 運算子安全地進行強制轉換(C# 編程指南). MSDN. http://msdn.microsoft.com/zh-cn/library/cc488006
2. as(C#參考). MSDN. http://msdn.microsoft.com/zh-cn/library/cscsdfbt
3. is(C#參考). MSDN. http://msdn.microsoft.com/zh-cn/library/scekt9xw
4. C# 中 as 和 is 的用法. http://www.cppblog.com/luyulaile/archive/2011/03/14/141773.html
5. C# as 與 is. http://blog.csdn.net/pengfeixiong/article/details/7409875