[英]Compiler generated sealed class for delegate keyword contains virtual methods
[英]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
。
因為FooGetter
是IFoo<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 Bryant和Andras 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.