精選文章

網站搬遷

 Hello all, 許久沒有寫部落格的習慣,未來會持續地在  https://alanzhan.dev/  更新

[C#] Effective - 03 偏好 is 或 as 運算子而非型別轉換

在C#這種強行別的語言
偶爾會需要做一個轉型的操作
Ex. Object 型態 轉換成其他型態


你有一些選擇
使用 as 運算子:強制編譯器,依照您的指示進行型別轉換
使用 is 運算子:先用 is 測試型別,再用as或其他方式轉換型別

如果 對於 is 或 as 不熟悉的,可以參考以下超連結,複習一下
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/is
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as

舉例來說,你要將你獲得的模糊物件轉換成MyType實例時,你可以這樣寫

或者你也可以這樣寫

到目前為止,你應該會比較同意第一段的寫法,因為它比較好閱讀,也沒有try catch語句,你只要檢查as後的值是否為null就好。
但是如果物件不是所要求的型別或繼承自所要求的型別時,as 就會失敗,此時可以使用implicit operator 將物件轉換成所要求的型別。

假設現在Factory.GetObject()方法,會回傳SecondType物件

以上兩個版本都會失敗,原因出在為 as operator 只關心在執行階段的時候,o 物件是否為MyType型態,而上面的o 物件 很明顯的不是 MyType 型態,所以會回傳null,而第二段,雖然有自定義轉型的方法,但是只在編譯階段有效,也是是說 o 在編譯階段並不是SecondType型態,所以執行階段 CLR 會依照預設的方式執行。
所以要讓 Effective-03-5.cs 成功執行自定義轉型方法,需要將程式碼做一下改寫。

可以看一下下面的程式碼,不論secondType 為甚麼型別,使用 as operator 可以得到較為一致的結果。
Mytype mytype = (MyType)secondType;
Mytype mytype  = secondType as MyType;

因為 as operator 可以獲得較為一致的結果,所以我們更應該使用 as operator ,但是 as operator 在下面的狀況,會發生無法編譯的狀況。
int i = o as int;
原因是出在於struct不能為null,所以我們應該用Nullable<T>來排除此問題,但是你如果又希望int? 必須含有值,這樣我會建議將o物件轉型完畢之後,做個檢查,如果為i = null時,跳出個例外!

現在大家應該對於 is、as 與 Cast之間的差異了吧?你認為foreach應該使用甚麼運算式嗎?foreach 支援可操作非泛型IEnumerale序列,並讓型態轉換在迭代中進行!(但是你更應該使用型別安全得泛型版本,非泛型版本因為歷史因素,而存在並於支援晚綁定狀況。)

上面的兩段程式碼,意思是相同的!
foreach 陳述需要型別轉換以支援class與struct。無論目標型別為何,透過選擇轉換運算式,foreach陳述展項出相同的行為,由於使用了轉換,foreach能夠引發InvalidCastException例外!

但是補充一下書上沒提及到的,如果你今天使用C# 7.0,我們可以更輕易的使用 is operator,讓程式碼更加的精簡,可以參考以下網址。
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#pattern-matching

可以看到上述程式碼
如果 is operator 判斷為true時,將會有一個val變數出現!此時你就可以不用再先用is判斷,再用as轉型!

總結

  1. 在一般的情況下,使用 as 運算式 比 Cast 更能得到一致的結果,也可以有更好的效能與可讀性。
  2. 在繼承的狀態下,無論 as 或者 Cast 都能從子類別轉型為父類別,如果要更進一步的比較型別,可以使用Object.GetType()。
  3. C# 7.0 可以使用 is operator 撰寫時更佳的輕鬆。

使用 is as 更能表現你這段程式碼的意圖!

留言

這個網誌中的熱門文章