簡體   English   中英

為什么在測試受約束的泛型類型時直接強制轉換失敗但“as”運算符成功?

[英]Why does a direct cast fail but the “as” operator succeed when testing a constrained generic type?

``在編譯一些使用具有類型約束的泛型的 C# 代碼時,我遇到了一個有趣的好奇心。 我寫了一個快速測試用例來說明。 我在 Visual Studio 2010 中使用 .NET 4.0。

namespace TestCast
{
    public class Fruit { }

    public class Apple : Fruit { }

    public static class Test
    {
        public static void TestFruit<FruitType>(FruitType fruit) 
            where FruitType : Fruit
        {
            if (fruit is Apple)
            {
                Apple apple = (Apple)fruit;
            }
        }
    }
}

轉換到 Apple 失敗並顯示錯誤:“無法將類型‘FruitType’轉換為‘TestCast.Apple’”。 但是,如果我更改該行以使用as運算符,則編譯時不會出錯:

Apple apple = fruit as Apple;

有人可以解釋為什么會這樣嗎?

我用這個問題作為2015 年 10 月一篇博客文章的基礎。 謝謝你的好問題!

有人可以解釋為什么會這樣嗎?

“為什么”的問題很難回答; 答案是“因為規范是這么說的”,然后自然的問題是“規范為什么這么說?”

所以讓我把問題說得更清楚:

哪些語言設計因素影響了使給定的強制轉換運算符在受約束的類型參數上非法的決定?

考慮以下場景。 您有一個基本類型 Fruit、派生類型 Apple 和 Banana,現在是重要的部分,用戶定義的從 Apple 到 Banana 的轉換。

當被稱為M<Apple>時,您認為這應該做什么?

void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)t;
}

大多數閱讀代碼的人會說這應該調用用戶定義的從 Apple 到 Banana 的轉換。 但是 C# 泛型不是 C++ 模板; 該方法不會為每個通用構造從頭開始重新編譯。 相反,該方法被編譯一次,並且在該編譯期間每個運算符的含義,包括強制轉換,為每個可能的泛型實例確定。

M<Apple>的主體必須具有用戶定義的轉換。 M<Banana>的主體將進行身份轉換。 M<Cherry>將是一個錯誤。 我們不能在泛型方法中擁有三個不同含義的運算符,因此該運算符被拒絕。

相反,您必須做的是:

void M<T>(T t) where T : Fruit
{
    Banana b = (Banana)(object)t;
}

現在這兩個轉換都清楚了。 到對象的轉換是隱式引用轉換; 到 Banana 的轉換是一個顯式的引用轉換。 用戶定義的轉換永遠不會被調用,如果這是用 Cherry 構造的,那么錯誤發生在運行時,而不是編譯時,就像對象轉換時一樣。

as運算符與 cast 運算符不同; 無論給出什么類型,它總是意味着相同的事情,因為as運算符永遠不會調用用戶定義的轉換。 因此,它可以用於強制轉換非法的上下文中。

“as 運算符就像一個強制轉換操作。但是,如果無法進行轉換,as 將返回 null 而不是引發異常。”

您不會收到as運算符的編譯時錯誤,因為編譯器在使用as運算符時不會檢查未定義的顯式強制轉換; 它的目的是允許嘗試可能有效或無效的運行時強制轉換,如果無效,則返回 null 而不是拋出異常。

無論如何,如果您打算處理fruit不是Apple ,您應該將您的支票實施為

var asApple = fruit as Appple;
if(asApple == null)
{
    //oh no
}
else
{
   //yippie!
}

回答為什么編譯器不允許您按照自己的意願編寫代碼的問題。 if 在運行時被評估,所以編譯器不知道只有當它有效時才會進行轉換。

為了讓它工作,你“可以”在你的 if 中做這樣的事情:

Apple apple = (Apple)(object)fruit;

這是關於同一問題的更多內容。

當然,使用as運算符是最好的解決方案。

它在 msdn 文檔中有解釋

as 運算符類似於強制轉換操作。 但是,如果無法進行轉換,as 將返回 null 而不是引發異常。 考慮以下示例:

expression as type 除了表達式變量只計算一次之外,代碼等價於以下表達式。

表達式是類型? (type)expression : (type)null 請注意, as 運算符僅執行引用轉換、可為空轉換和裝箱轉換。 as 運算符不能執行其他轉換,例如用戶定義的轉換,而應該使用強制轉換表達式來執行這些轉換。

基類類型的變量可以保存派生類型。 要訪問派生類型的方法,必須將值強制轉換回派生類型。 使用 as 將阻止您獲得 InvalidCastException。 如果你想處理特定的空引用場景,你可以這樣做。

public class Fruit
{
    public static explicit operator bool(Fruit D)
    {
         // handle null references
         return D.ToBoolean();
    }

    protected virtual bool ToBoolean()
    {
         return false;
    }
}

AS運算符關鍵字從 Visual Basic 繼承其操作。

知情人士會告訴您,Visual Basic 是一種比 C# 功能更強大的語言,C# 本身就是在不斷嘗試制作類似 C 的語法語言,但具有 Visual Basic 的功能

這是因為 C 語法語言更受專業人士歡迎,而那些不標榜自己是 Basic 的語言也是如此。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM