簡體   English   中英

為什么我不能將此接口轉換為具體的類?

[英]Why can't I cast this interface to a concrete class?

我有一個接口IApiDataWithProperties 名為Event的類實現此接口。

通常,我可以將IApiDataWithProperties對象IApiDataWithPropertiesEvent (假設它是一個),並且讓編譯器讓我做到這一點沒有問題。

在這種情況下,類型實際上是通用的TApiData ,它具有連接IApiDataWithPropertieswhere限制。 但是,即使有類型限制,我也無法將TApiDataEvent 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限制保證TApiDataIApiDataWithProperties ; 它不保證有關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.

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