首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

Effective C# 原則 3:善用 is 與 as 運算子,避免強制類型轉換

     編輯:關於C#

C# 是一門強型別語言,良好的程式設計實踐建議我們在可能的情況下避免類型之間的強制轉換。然而,執行時期的型別檢查有時無法避免,特別是當我們必須處理 System.Object 類型的參數,並嘗試將其向下轉型為特定介面或類別時。此時我們有兩種主要選擇:使用 as 運算子,或是採用傳統的 C 風格強制轉型。無論採用哪種方式,都必須對變數進行防護性檢查。

為何 as 運算子是更佳選擇

正確的做法是優先使用 as 運算子進行型別轉換。原因有二:第一,它比盲目強制轉型更安全;第二,它在執行時期效率更高。as 和 is 運算子僅在執行時期型別與目標型別相符(或為其衍生型別)時才成功轉換,絕不會建立新物件來滿足轉換需求。

程式碼比較:as vs 強制轉型

假設我們有一個方法 Factory.GetObject() 回傳任意型別的物件,我們想將其轉為 MyType。

版本一:使用 as 運算子(推薦)

object o = Factory.GetObject();
MyType t = o as MyType;
if (t != null)
{
    // 成功轉換,t 是 MyType
}
else
{
    // 轉換失敗處理
}

版本二:使用強制轉型 + try/catch

object o = Factory.GetObject();
try
{
    MyType t = (MyType)o;
    if (t != null)
    {
        // 成功轉換
    }
    else
    {
        // null 參考處理
    }
}
catch
{
    // 轉換失敗處理
}

第一種寫法更簡潔、易讀,且避免了 try/catch 結構帶來的效能開銷與程式碼雜訊。值得注意的是,null 可以被強制轉型為任何參考型別,但 as 運算子在轉換 null 參考時會安全地回傳 null,因此只需簡單檢查結果是否為 null 即可。

使用者定義型別轉換的陷阱

as 和 is 運算子在執行時期檢查實際物件型別,若物件並非目標型別或其衍生類別,轉換失敗。相反地,強制轉型會嘗試呼叫使用者定義的轉換運算子,這可能導致非預期的行為。

例如,若 SecondType 定義了隱含轉換運算子至 MyType:

public class SecondType
{
    private MyType _value;
    public static implicit operator MyType(SecondType t) => t._value;
}

當 Factory.GetObject() 回傳 SecondType 實例時:

  • o as MyType 失敗(回傳 null),因為執行時期型別是 SecondType,非 MyType 或其衍生類。
  • (MyType)o 同樣失敗,因為編譯器只知道 o 的編譯時期型別是 object,找不到從 object 到 MyType 的使用者定義轉換,故產生型別檢查程式碼,執行時期發現不符而拋出例外。

若要成功轉換,必須先將 object 轉為 SecondType,再套用強制轉型:

SecondType st = o as SecondType;
MyType t = (MyType)st; // 呼叫使用者定義轉換運算子

這凸顯了強制轉型在編譯時期綁定轉換邏輯的特性,容易產生難以除錯的問題。

值型別的限制與 is 的應用

as 運算子不適用於值型別(如 int),因為值型別不可為 null,轉換失敗時無法表示「無值」狀態。此時應改用 is 運算子進行型別檢查,再進行強制轉型:

object o = Factory.GetValue();
int i = 0;
if (o is int)
    i = (int)o;

is 運算子同樣安全,不會拋出例外,且對 null 永遠回傳 false。只有在無法使用 as 時(如值型別轉換)才應使用 is。

避免冗余檢查

以下寫法屬於多餘且低效:

// 冗余:雙重檢查
if (o is MyType)
    t = o as MyType;

// 同樣冗余
if ((o as MyType) != null)
    t = o as MyType;

使用 as 時,直接檢查結果是否為 null 即可,無需額外 is 檢查。

foreach 迴圈的隱含強制轉型

foreach 迴圈內部使用強制轉型將 IEnumerator.Current(型別為 object)轉為迴圈變數型別。這意味著若集合元素實際型別雖可轉換但非目標型別或其衍生類,foreach 仍會拋出 InvalidCastException。例如,集合儲存 SecondType 但迴圈宣告為 MyType,轉換將失敗。

精確型別比較:GetType()

若需判斷物件是否為「確切」某型別(而非衍生類),應使用 obj.GetType() == typeof(MyType)。as 和 is 對衍生類實例也會回傳 true,語義上較寬鬆。在大多數多型情境下,is/as 的語義已足夠;僅在相等性比較等需精確型別時才需 GetType()。

總結

良好的物件導向實踐雖建議避免型別轉換,但必要時請優先使用 as(參考型別)或 is 搭配強制轉型(值型別)。它們語義清晰、行為可預期,且能避免強制轉型帶來的非預期副作用與效能損耗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved