[英]Why can't I cast this interface to a concrete class?
我有一個接口IApiDataWithProperties
。 名為Event
的類實現此接口。
通常,我可以將IApiDataWithProperties
對象IApiDataWithProperties
為Event
(假設它是一個),並且讓編譯器讓我做到這一點沒有問題。
在這種情況下,類型實際上是通用的TApiData
,它具有連接IApiDataWithProperties
的where
限制。 但是,即使有類型限制,我也無法將TApiData
為Event
。 我Cannot convert type 'TApiData' to 'Event'
為什么是這樣? 我想念什么嗎?
public class Event : IApiDataWithProperties, IXmlSerializable
{
// ...
}
public abstract class AbstractBatchPropertyProcessor<TApiData> : AbstractBatchProcessor<TApiData>, IBatchProcessor
where TApiData : IApiDataWithProperties
{
protected virtual string Build(ConcurrentBag<TApiData> batch)
{
foreach (var newItem in batch)
{
if(newItem is Event)
{
// This cast fails: Cannot convert type 'TApiData' to 'Event'
((Event)newItem).Log();
}
}
// ...
}
}
編輯 :
我只是想知道為什么這是編譯錯誤。
我知道這是一個奇怪的設計,並且您通常不會在通用方法中那樣進行強制轉換。 這是我在測試期間想要在其中添加一些快速日志記錄信息時遇到的,這是最省力的方法。
原因是newItem
可以是實現IApiDataWithProperties
任何類型,因此編譯器不能保證其類型可以轉換為Event
。 即使您使用is
運算符檢查它,對編譯器也沒有任何意義。 作為解決方法,您可以使用double cast :
((Event)(object)newItem).Log();
即使此方法有效,也不意味着您應該使用它。 您不應該在通用方法中檢查類型。 而是嘗試使用多態,將Log
方法添加到IApiDataWithProperties
或其他接口,並在您的類型中實現它。 然后對該接口設置另一個約束,然后您可以調用該方法而無需強制轉換。
簡短版-您可以使用:
(newItem as Event).Log();
而不是您的演員
TApiData
版本-您的TApiData
對象實現了IApiDataWithProperties
因此它可以是Event
,但可以是實現它的其他所有方法。 這稱為向下轉換 ,必須在運行時借助as
/ is
運算符完成。 編譯器在編譯期間不知道泛型類型的newItem
是否確實是Event
因此它無法確保此類轉換。
因為where
限制保證TApiData
是IApiDataWithProperties
; 它不保證有關IApiDataWithProperties
實現的類型。
更多:
每個Child
都是Parent
的實例,反之亦然。 考慮一下這個模型:
interface I { void InterfaceNethod(); }
class A : I {
void InterfaceMethod() { }
void AMethod() { }
}
class B : I {
void InterfaceMethod() { }
void BMethod() { }
}
現在,讓我們獲取一些實例,並調用其方法:
I i = new A();
i.InterfaceMethod(); // it works
i.AMethod(); // it doesn't work, cause I has not a method named AMethod
現在,鑄件:
A a = new A();
a.InterfaceMethod(); // exists
a.AMethod(); // exists
I i = (I)a; // correct
i.InterfaceMethod(); // exists
B b = (B)i;
// if this be correct, then we should be able to call B's methods on b, right?
// While b hasn't any of B's members.
// I mean calling this:
b.BMethod();
// is logically incorrect. right? because, following the object's reference in memory
// would tell us that b is pointing to an A instance actually. Am I right?
// so the cast will fail. Because compiler knows about logic :) a little bit at least. cheers
您可以在IApiDataWithProperties接口內聲明Log方法。 在Event類中實現Log方法,然后在獲取異常的位置將newItem強制轉換為IApiDataWithProperties而不是Event。
((IApiDataWithProperties)newItem).Log
或實際上考慮它,您可能不需要演員表就可以
newItem.Log
您的設計並不是最好的開始,因為它假設特定類的實現。 但是如果您希望這樣做的話...
使用as
可以工作,但您可能希望它也限於引用類型。
where TApiData : IApiDataWithProperties, class
...
(newItem as Event).Log();
盡管這並不完全安全,因為它可能導致null,但您可能還應該檢查一下
var item = newItem as Event;
if(item != null)
item.Log();
您也可以消除if(newItem is Event)
因為它不是必需的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.