簡體   English   中英

我可以將基本 class 類型作為通用參數傳遞給接口嗎

[英]Can I pass a base class type as a generic parameter to an interface

我可以將基本 class 類型作為通用參數傳遞給接口,這些接口在 C# 的基本 class 的后繼者的上下文中實現。

假設有一個接口:

public interface IElement<T>
{
   Collection<T> Neighbors { get; }   
}

我想像這樣實現這個接口:

public class Element : IElement<object>
{
   public Collection<Neighbor> Neighbors { get; set; }
}

我什至也嘗試過這樣實現它:

public class Element : IElement<IObject>
{
   public Collection<Neighbor> Neighbors { get; set; }
}

其中 IObject 和 Neighbor 是:

public interface IObject
{ 
}

public class Neighbor : IObject
{
   // some props
}

無論哪種方式,我都會遇到相同的錯誤: Element 沒有實現 IElement 接口 - 成員 Neighbors 無法實現 IElement.Neighbors,因為它們具有不同的返回類型。

這樣做的目的是對數據類型進行抽象並在業務邏輯中使用它們的更簡單版本:

public class BusinessLogic
{
   bool predicate1;
   bool predicate2;
    
   // variant 1
   public bool CanIDoSomething(IElement<object> element)
   {
      return element.Neighbours.Any() && predicate1 && predicate2;
   }

   // variant 2
   public bool CanIDoSomething(IElement<IObject> element)
   {
      return element.Neighbours.Any() && predicate1 && predicate2;
   }
}

如果我嘗試使用 IEnumerable 而不是 Collection,問題會更嚴重:

public interface IElement<T>
{
   IEnumerable<T> Neighbors { get; }   
}

我錯過了什么,是否有解決方法?


編輯:

我使 IElement 接口泛型參數協變如下:

public interface IElement<out T>
{
   IEnumerable<T> Neighbors { get; }
}

但這並不能解決接口實現錯誤。

我可以使 class 正確實現接口的唯一方法是這樣做:

public class Element : IElement<IObject>
{
   public Collection<Neighbor> Neighbors { get; set; }

   IEnumerable<IObject> IElement<IObject>.Neighbors => Neighbors;
}

在實現接口時是否存在導致協方差不被隱式應用的限制?

有沒有更優雅的方式來做到這一點?

我想保留IObject接口而不是直接使用object ,因為如果需要,我可以在IObject接口中放置Neighbor class 屬性的某個子集。

Collection<T>的原始場景

您會收到此錯誤,因為您的IElement<T>接口具有泛型類型參數T並且屬性Neighbors的類型為Collection<T> 因此,使用具體類型object實現IElementElement類型需要實現Collection<object>而不是Collection<Neighbor> 此屬性必須是public的,因為它是從您的公共接口實現的,並且class中的默認成員可見性是private的。 如果將object替換為Neighbor或任何其他類型,這同樣適用。 請參閱此示例Neighbor

public class Element : IElement<Neighbor>
{
    public Collection<Neighbor> Neighbors { get; set; }
}

雖然這解決了您最初的錯誤,但您將無法將Element類型的實例傳遞給您的方法CanIDoSomething ,因為IElement<T>中的類型參數Tinvariant ,這意味着您只能使用最初指定的類型。 在示例中,此類型為Neighbor而不是IObjectobject

您需要使您的類型參數協變才能使用更派生的類型,例如Neighbor 但是,您會遇到Collection<T>也使用不變類型參數的問題,因此您需要將其替換為具有協變類型參數的類型,例如IReadOnlyCollection<T>IEnumerable<T> 下面的示例將向您展示協方差如何適用於您的案例。 您可以從那里開始使用其他類型和 collections。

generics 和IEnumerable<T>的場景

如果您希望IElement<T>是通用的並使用IEnumerable<T> ,請使用關鍵字out使用可變類型參數聲明您的接口。

public interface IElement<out T>
{
    IEnummerable<T> Neighbors { get; }
}

然后用正確的類型在你的具體 class 中實現它,例如Neighbor

public class Element : IElement<Neighbor>
{
    public IEnumerable<Neighbor> Neighbors { get; }
}

您可以保留兩個CanIDoSomething方法,因為Neighbor派生自object並實現IObject接口,因此它在兩種情況下都可以使用協方差。

暫無
暫無

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

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