多用as少用強制型別轉換
在 C# 中存在一個名叫靜態型別檢查的機制,這個機制可以讓編譯器幫助我們把型別不服的用法找出來,從而使得應用程式在執行期間加少一些型別檢查的操作。但是有時候我們還是需要進行執行期型別檢查,比如我們在設計框架時將方法的引數型別定義為 object ,那麼這時我們就有很大的可能需要將 object 型別的引數先轉換為其他型別。我們進行轉換時會有兩種方法可以使用:一種是強制型別轉換,這種方法可以繞過編譯器的型別檢查,另一種是先通過 is 判斷操作是否合理,是否可以轉換,然後再使用 as 運算子進行轉換,或者使用強制型別轉換。下面我們就來講解一下為什麼多使用 as 少使用強制型別轉換。
零、as and is
使用 as 進行型別轉換會比強制型別轉換更加安全,而且執行時效率更高。但是這裡有一點需要注意的是 as 和 is 運算子不會考慮使用者所定義的型別轉換,只有當執行期的型別與要轉換到的型別相符時才能順利進行。一般來說 as 型別轉換很少會出現為了型別轉換而建立新的物件,只有在 as 運算子把裝箱值型別轉換未裝箱且可以為 null 的型別時才會建立新物件。
is 運算子遵循多型原則,也就是說例如 變數 Husky(哈士奇)是 Dog 型別,並且 Dog 型別繼承自 Animal 型別,那麼 程式碼段 husky is Animal
返回值就是 True 。因此我們可以利用這一特性來判斷某個物件是否是某個具體型別。當然我們也可用通過 GetType 方法來查詢物件的執行期型別,這樣可以使開發人員寫出比 as 和 is 更加具體更加詳細的型別,這主要歸功於它所返回的物件型別能夠和某種特定型別進行對比。
一、為什麼不用強制型別轉換
我們先來看一段程式碼:
try
{
object obj = Factory.GetObject();
Animal animal;
animal = (Animal) obj;
if (animal !=null)
{
// more code
}
// more code
}
catch (InvalidCastExcept ex)
{
// more code
}
在上述程式碼中我們使用了強制型別轉換將 object 型別的變數轉換為 Animal 型別,我相信部分開發人員在實際開發中都會這麼寫,這麼些也不為過,但是這其中存在一個問題,開發人員需要處理兩個問題。首先程式如果無法將變數 obj 轉換為 Animal 型別將丟擲 InvalidCastException 異常,因此我們必須捕獲,其次在強制型別轉換時遇到 null 的時候並不會丟擲異常,因此我們還要判斷變數 animal 是否為 null 。既然強制型別轉換有這個問題,那我們該如何解決呢?這時我們就可以用到 as 和 is 運算子了,同樣我們先看一下程式碼:
try
{
object obj = Factory.GetObject();
if (obj is Animal)
{
Animal animal = obj as Animal;
// more code
}
else
{
// more code
}
}
利用這種方法我們首先判斷 obj 是否可以轉換為 Animal 型別,如果可以就利用 as 運算子來轉換,反之執行其他程式碼。既不需要捕獲錯誤,也不需要強制轉換,減少了程式碼量同時也減少了程式碼出錯的機率。
as 運算子和強制型別轉之間有一個很大的區別,那就是如何對待使用者自定義的轉換邏輯。 as 和 is 運算子除了必須進行的裝箱和拆箱外,它不會執行其他任何操作,也就是說 as 和 is 只會判斷帶轉換物件在執行期是什麼型別,並根據結果進行相應的處理。那麼如果帶轉換物件既不屬於目標型別也不屬於目標型別所派生出來的型別的話, as 操作就宣告失敗。強制型別轉換則不然,它有可能使用一些型別的轉換邏輯進行型別轉換,而且不僅僅是使用者自定義的轉換邏輯,還包含了內建型別之間的轉換。但是要注意的是強制型別轉換可以會造成資訊丟失,例如從 long 強制轉換為 short 。
在某些情況下利用強制型別轉換從程式碼上來看似乎可以轉換成功,但實際上卻轉換不成功。這時為什麼呢?雖然強制型別轉換會把使用者自定義的轉換邏輯考慮進去,但是它只針對物件的編譯期型別,編譯期型別並不是是基型別。例如帶轉換型別在編譯期是 object 型別,因此編譯器會將它看作 object ,這時如果進行強制型別轉換的話就會報錯。
前面說了那麼多使用 as 的好處,那麼在這一小節裡我們就來說說在什麼時候不能使用 as 和 is 。同樣,先來看一小段程式碼:
object obj =Factory.GetValue();
int num = obj as int;
上面的這段程式碼執行起來後將會報錯,為什麼呢?這是因為當 obj 不是 int 型別時返回的值是 null ,但是 int 型別無法接受 null 值。因此當指定型別不可接受 null 值時 as 無法進行型別轉換。
二、一個問題
下面我們再思考一個問題,我們都知道 foreach 所針對的序列是非泛型序列它會在迭代過程中自動轉換,那麼 foreach 的型別轉換使用的是 as 呢還是強制型別轉換呢?
foreach 使用的時強制型別轉換,會把物件從 object 型別轉換成迴圈體所需要的型別,之所以使用強制型別轉換是因為 foreach 需要同時應對值型別和引用型別。
三、總結
在開發中我們應該儘量避免使用強制型別轉換,強制型別轉換在某些情況下可能會出現開發人員預料之外的結果,使用 as 和 is 運算子可以確保物件確實可以進行型別轉換時才給出答案,這樣可以保證程式的正確性。
本文由部落格一文多發平臺 OpenWrite 釋出!更多文章掃碼關注“喵叔呦”