<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 原則3:選擇 is 或 as 而不是強制類型轉換 **By D.S.Qiu** 尊重他人的勞動,**支持原創,轉載請注明[出處](/blog/1977594):[http://dsqiu.iteye.com](http://dsqiu.iteye.com)** 當你使用 C# ,你就應該知道它是強類型語言。大多時候,這總是一個好事。強類型意味著編譯器可以發現你的代碼中的類型是否匹配。也可以減少運行時的類型檢查。但是有些時候,類型檢查是不可避免的。很多時候,函數的調用參數是 object ,因為在框架中定義好了函數原型。你很有可能要進行強制類型轉換(cast)為其他類型的類或接口。你可以用兩種選擇:使用 as 操作符或者強制類型轉換。在轉換之前,你需要進行變量類型防護:使用 is 進行類型判斷,然后再用 as 進行轉換或者強制類型轉換。 無論什么時候都應該選擇 as 操作符,因為它比盲目的強制類型轉換更安全且運行時更高效。 as 和 is 不會執行任何用戶自定義的轉換。只有當目標類型和運行時類型匹配時才會成功轉換;它不會構造一個新對象去滿足需求。 看一個例子。你需要些一段代碼將任意類型的對象轉換為 MyType 的實例。你可能會這樣寫: ``` object o = Factory.GetObject(); // Version one: MyType t = o as MyType; if (t != null) { // work with t, it's a MyType. } else { // report the failure. } ``` 或者,你會這樣寫: ``` object o = Factory.GetObject(); // Version two: try { MyType t; t = (MyType)o; // work with T, it's a MyType. } catch (InvalidCastException) { // report the conversion failure. } ``` 你會覺得第一個版本的寫法簡單而可讀性強。它沒有 try/catch 代碼塊,所以可以同時避免性能開銷和代碼量。注意到強制類型轉換除了要捕捉異常之外還要堅持是否為 null 。null 類型可以強制轉換為任意引用類型,但 as 操作符會返回 null 。所以強制類型轉換要檢查 null 和捕捉異常。而使用 as ,你只要很簡單檢查返回引用是否為 null 。 強制類型轉換和 as 操作符轉換最大的區別在于如何看待用戶自定義的類型轉換。 as 和 is 操作符在運行時要轉換對象的類型,不會進行額外的操作。如果一個類型不是轉換要求的類型或者是其子類的話,轉換就會失敗。然而,強制類型轉換可以使用轉換操作符轉換一個對象到需要的類型。這就包括了內建數據類型的轉換。強制轉換一個 long 整數到 short 整數會丟失信息。 同樣的問題也會在你自定義的類型中出現。考慮下面的類型: ``` public class SecondType { private MyType _value; // other details elided // Conversion operator. // This converts a SecondType to // a MyType, see item 9\. public static implicit operator MyType(SecondType t) { return t._value; } } ``` 假設, Factory.GetObject() 會返回上一段代碼的 SecondType 的對象: ``` object o = Factory.GetObject(); // o is a SecondType: MyType t = o as MyType; // Fails. o is not MyType if (t != null) { // work with t, it's a MyType. } else { // report the failure. } // Version two: try { MyType t1; t1 = (MyType)o; // Fails. o is not MyType // work with t1, it's a MyType. } catch (InvalidCastException) { // report the conversion failure. } ``` 兩種版本的代碼都會失敗。但是強制類型轉換執行的是用戶自定義的轉換。你應該會覺得會成功的。你是對的——是會成功的,如果按你想的方式。但是還是失敗了——因為編譯器產生的代碼是基于編譯時的對象類型, o 。編譯器對運行時 o 的類型一無所知; o 只是 object 的對象。編譯器沒有找到用戶定義的從 object 轉換為 MyType 的方法。它只是檢查了 object 和 MyType 的類型定義。編譯器沒有發現用戶定義的類型轉換,會產生在運行時檢查 o 的類型是否是 MyType 的代碼。因為 o 是 SecondType 對象,所以會失敗。編譯器不會檢查 o 的實際運行時類型是否可以轉換為 MyType 對象。 下面代碼塊可以成功將 SecondType 轉換為 MyType : ``` object o = Factory.GetObject(); // Version three: SecondType st = o as SecondType; try { MyType t; t = (MyType)st; // work with T, it's a MyType. } catch (InvalidCastException) { // report the failure. } ``` 你應該杜絕寫這么糟糕的代碼,但這確實解決一個常見的問題。雖然不應該這樣寫代碼,但可以用 object 參數來實現正確的轉換: ``` object o = Factory.GetObject(); DoStuffWithObject(o); private static void DoStuffWithObject(object o) { try { MyType t; t = (MyType)o; // Fails. o is not MyType // work with T, it's a MyType. } catch (InvalidCastException) { // report the conversion failure. } } ``` 只需記住用戶自定義的類型轉換只針對編譯時期的類型,而不會對運行時類型起作用。這不會影響 o 運行時類型和 MyType 轉換的存在。編譯器并不會知道和在意。下面語句會有不同的行為根據 st 不同的聲明類型: ``` t = (MyTpe)st ``` 下面語句無論 st 聲明是什么類型得到的結果都是一樣的。所以,應該更偏向使用 as 而不是強制類型轉換——它得到的結果更一致。實際上,這次類型沒有繼承關系,而且用戶自定義轉換存在,下面的語句會產生一個編譯錯誤: ``` t = st as MyType; ``` 既然你知道使用盡可能使用 as ,下面我們討論什么時候是不能使用的。 as 操作符是不能再值類型上使用的。這條語句不能通過編譯: ``` object o = Factory.GetValue(); int i = o as int; // Does not compile. ``` 這是因為 int 是值類型,不能賦為 null 。那如果 o 不是整數 int 變量 i 會取什么值。無論取什么值都是無效的整數。因此, as 不能使用,只有使用強制類型轉換語法。這實際是一個裝箱(boxing)或拆箱(unboxing)的轉換(查看原則45)。 ``` object o = Factory.GetValue(); int i = 0; try { i = (int)o; } catch (InvalidCastException) { i = 0; } ``` 流控制機制的異常是一個非常糟糕的做法。但你又不得不使用強制類型轉換的行為。你可以使用 is 語句去掉可能引起的異常或轉換: ``` object o = Factory.GetValue(); int i = 0; if (o is int) i = (int)o; ``` 如果 o 不是可以轉換為 int 的其他類型,比如 double ,上面的 is 操作會返回 false。參數為 null , is 總是返回 false 。 is 操作符只應該用于無法使用 as 來轉換的情況。否則只是多余: ``` // correct, but redundant: object o = Factory.GetObject(); MyType t = null; if (o is MyType) t = o as MyType; ``` 上面的代碼和下面寫的代碼是一樣的: ``` // correct, but redundant: object o = Factory.GetObject(); MyType t = null; if ((o as MyType)) != null) t = o as MyType; ``` 這是低效并且多余的。如果你打算用 as 轉換類型, is 類型檢查簡單但卻沒有必要的。檢查 as 的返回值是否為 null ,更簡單。 既然掌握了 is , as 和強制類型轉換的區別了,那么哪個操作應該在 foreach 循環中使用? foreach 循環可以操作非泛型 IEnumerable 序列和內建強制類型轉換為迭代器(iteration)。(你應該盡可能使用類型安全泛型版本。非泛型版本的存在處于歷史目的和支持晚綁定情況)。 ``` public void UseCollection(IEnumerable theCollection) { foreach (MyType t in theCollection) t.DoStuff( ); } ``` foreach 使用強制類型轉換操作轉換對象到循環體中使用的類型。 foreach 語句幾乎等同于下面手動實現的版本: ``` public void UseCollectionV2(IEnumerable theCollection) { IEnumerator it = theCollection.GetEnumerator(); while (it.MoveNext()) { MyType t = (MyType)it.Current; t.DoStuff(); } } ``` foreach 的強制類型轉換需要同時支持 值類型和引用類型。因為使用強制類型轉換,foreach 語句呈現的一樣的行為,無論目標類型是什么。因為使用了強制類型轉換, foreach 循環會引起并拋出 InValidCastException 異常。 你為 IEnumerator.Current 返回的是 System.Object 類型,沒有任何轉換操作,上面的測試是不合格的。 SecondType 的對象集合不能使用在上面的 UseCollection 函數,因為依你所見強制類型轉換會失敗。 foreach 語句(使用強制類型轉換)不會檢查集合中強制類型轉換的對象的運行時類型。只是檢查 System.Object 類( IEnumerator.Current 返回的類型)和聲明的循環變量類型(在上面例子中的 MyType )轉換是否可行。 最后,有時你想知道對象的具體類型,而不只是當前類型能不能轉換為目的類型。 is 操作符使用在任何繼承自目標類型的對象都會返回 ture 。GetType() 方法可以獲得對象的運行時類型。它提供了比 is 和 as 更嚴格的類型測試。 GetType() 返回一個對象的類可以拿來和指定的類型比較。 再次考慮這個函數: ``` public void UseCollectionV3(IEnumerable theCollection) { foreach (MyType t in theCollection) t.DoStuff(); } ``` 如果你創建一個繼承 MyType 的類 NewType , NewType 的集合可以在 UnseConllection 函數上正確工作: ``` public class NewType : MyType { // contents elided. } ``` 如果你想要寫一個函數使得所有 MyType 對象都能工作,上面的方法已經可以了。如果你想要這個方法只對 MyType 對象正常工作,你應該使用精確類型比較。這里你可以在 foreach 循環中實現。很多時候,精確的運行時類型對于相等測試是非常重要的(查看原則6)。很多其他比較, as 和 is 提供的 .isinst 指令在語意上是正確的。 .NET 繼承類庫(BCL)包含一個使用相同類型操作轉換序列中元素的方法: Enumerble.Cast&lt;T&gt;() 轉換支持實現 IEnumerale 接口的類的序列的每個元素。 ``` IEnumerable collection = new List<int>() {1,2,3,4,5,6,7,8,9,10}; var small = from int item in collection where item < 5 select item; var small2 = collection.Cast<int>().Where(item => item < 5). Select(n => n); ``` 上面代碼的最后一行的查詢產生相同方法調用。這兩個例子, Cast&lt;T&gt; 方法都是轉換序列中每個元素到目標類型。 Enumerable.Cast&lt;T&gt; 方法使用的是舊的強制類型轉換而不是 as 操作符。舊的強制類型轉換說明 Cast&lt;T&gt; 不要需要包含類型約束。使用 as 操作符會會有限制,為了不用實現不同的 Cast&lt;T&gt; 方法, BCL 團隊選擇使用了舊的強制類型轉換操作來只產生一個方法。這是你在寫代碼是需要權衡的。當你需要轉換一個泛型參數的對象時,你需要權衡類型約束必要性和強制類型轉換的不同行為。 在 C# 4.0,類型系統可以通過使用動態類型或運行時檢查來規避。這也是第5章的目的,“ C# 動態編程”。有很多方法可以預期知道對象的行為而不需要知道對象實現的類或接口。你將要學習什么時候該使用這些技術什么時候該避免。 好的面向對象實踐告訴我們應該避免使用類型轉換,但是有時候卻別無選擇。如果你不能避免使用類型轉換,使用語言提供的 as 和 is 操作符來清晰地表達你的用意。不同方式的強制類型轉換有不同的規則。 is 和 as 操作符幾乎總是正確的語義,只有當然測試的對象是正確的類型才會成功。選擇這些語句來轉換類型而不是強制類型轉換,因為能返回你期望的成功或失敗,而不會有意想不到的影響。 小結: 這篇文字量比較多,今天上班也比較累,狀態不好,翻譯的感覺也不是很順,畢竟腦子轉的慢了,本來不想寫的,晚上下班的時候剛好跟同事聊起強制類型轉換和 as 轉換。所以才會鼓足勁寫完,白天的時候多看看,這篇感覺作者很多細節還是沒有將清楚,看了第一版的翻譯就該知道——作者寫了很多注釋,加了自己的理解。由于時間,精力和需求,暫時我就不做具體的代碼測試了,好快呀,又到兩點了,頭也有點暈,所幸的是憋完了,這周的工作內容也很重,加油! 歡迎各種不爽,各種噴,寫這個純屬個人愛好,秉持”分享“之德! 有關本書的其他章節翻譯請[點擊查看](/category/297763),轉載請注明出處,尊重原創! 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。 轉載請在**文首**注明出處:[http://dsqiu.iteye.com/blog/1977594](/blog/1977594) 更多精彩請關注D.S.Qiu的 博客 和微博(ID:靜水逐風)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看