簡體   English   中英

密封關鍵字會影響編譯器對強制轉換的看法

[英]Sealed keyword affects the compiler's opinion on a cast

我有一種情況,我喜歡編譯器的行為解釋。 給出一點代碼:

interface IFoo<T>
{
    T Get();
}

class FooGetter : IFoo<int>
{
    public int Get()
    {
        return 42;
    }
}

以下編譯並運行:

static class FooGetterGetter
{
    public static IFoo<T> Get<T>()
    {
        return (IFoo<T>)new FooGetter();
    }
}

如果我們更改Foo類的簽名並添加sealed關鍵字:

sealed class FooGetter : IFoo<int> // etc

然后我在以下行得到編譯器錯誤:

 return (IFoo<T>)new FooGetter();

的:

無法將類型'MyNamespace.FooGetter'轉換為'MyNamespace.IFoo <T>'

有人可以解釋一下有關sealed關鍵字的問題嗎? 這是針對Visual Studio 2010中的.NET 4項目的C#4。

更新:有趣的是,當我想知道為什么以下代碼在應用sealed時修復它時,我偶然發現了這一部分行為:

return (IFoo<T>)(IFoo<int>)new FooGetter();

更新:只是為了澄清,這一切都運行良好時的類型T要求是一樣的類型T的具體類型使用。 如果類型不同,則轉換在運行時失敗,例如:

無法將“MyNamespace.StringFoo”類型的對象強制轉換為“MyNamespace.IFoo”1 [System.Int32]'

在上面的示例中, StringFoo : IFoo<string>並且調用者要求獲取int

因為FooGetterIFoo<int>的顯式實現,而不是一般地實現IFoo<T> 由於它是密封的,編譯器知道如果T不是int ,那么就沒有辦法將它轉換為通用的IFoo<T> 如果沒有密封,編譯器將允許它在運行時編譯並拋出異常,如果T不是int

如果你嘗試使用除int之外的任何東西(例如FooGetterGetter.Get<double>(); ),你會得到一個例外:

無法將“MyNamespace.FooGetter”類型的對象強制轉換為“MyNamespace.IFoo”1 [System.Double]'。

什么我不知道的是為什么編譯器產生非密封版本錯誤。 您的子類FooGetter如何使new FooGetter()為您提供實現IFoo<{something_other_than_int}>

更新:

Per Dan BryantAndras Zoltan有一些方法可以從構造函數返回派生類(或者更准確地說, 編譯器通過分析屬性返回不同的類型)。 所以從技術上講,如果班級沒有密封,這是可行的。

當未密封任何派生類的類可以實現IFoo<T>

class MyClass : FooGetter, IFoo<double> { }

FooGetter被標記為密封時,編譯器知道FooGetter可能存在除IFoo<int>之外的任何其他IFoo<T> FooGetter

這是一種很好的行為,它允許您在編譯時而不是在運行時捕獲代碼問題。

之所以(IFoo<T>)(IFoo<int>)new FooGetter(); 工作是因為您現在將密封類表示為IFoo<int> ,可以通過任何方式實現。 這也是一個很好的工作,因為你不是偶然的,但有目的地重寫編譯器檢查。

只是為了增加現有答案:這與使用的泛型無關。

考慮這個更簡單的例子:

interface ISomething
{
}

class OtherThing
{
}

然后說(在方法內):

OtherThing ot = XXX;
ISomething st = (ISomething)ot;

工作得很好。 編譯器不知道OtherThing是否可能是一個ISomething ,所以當我們說它會成功時它會相信我們。 但是,如果我們將OtherThing更改為密封類型 (即sealed class OtherThing { }struct OtherThing { } ),則不再允許轉換。 編譯器知道它不能順利(除非ot均是null的,但C#的規則仍然禁止從密封型投在不被密封型實現的接口)。

關於問題的更新:寫入(IFoo<T>)(IFoo<int>)new FooGetter()與寫入(IFoo<T>)(object)new FooGetter()沒有太大的不同。 您可以通過一些中間類型“允許”任何演員(使用泛型或不使用),這些中間類型當然/可能是您要在兩種類型之間轉換的類型的祖先。 它與這種模式非常相似:

void MyMethod<T>(T t)    // no "where" constraints on T
{
  if (typeof(T) = typeof(GreatType))
  {
    var tConverted = (GreatType)(object)t;
    // ... use tConverted here
  }
  // ... other stuff
}

暫無
暫無

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

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