简体   繁体   English

我可以将基本 class 类型作为通用参数传递给接口吗

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

Can I pass base class types as a generic parameters to interfaces which are implemented in the context of the successors of that base class in C#.我可以将基本 class 类型作为通用参数传递给接口,这些接口在 C# 的基本 class 的后继者的上下文中实现。

Let's imagine there is an interface:假设有一个接口:

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

I want to implement this interface like this:我想像这样实现这个接口:

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

I also even tried achieving it like this:我什至也尝试过这样实现它:

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

Where IObject and Neighbor are:其中 IObject 和 Neighbor 是:

public interface IObject
{ 
}

public class Neighbor : IObject
{
   // some props
}

In either way I get the same error: Element does not implement the IElement interface - member Neighbors cannot implement IElement.Neighbors, because they have different return types.无论哪种方式,我都会遇到相同的错误: Element 没有实现 IElement 接口 - 成员 Neighbors 无法实现 IElement.Neighbors,因为它们具有不同的返回类型。

The purpose of this is to make an abstraction of the datatypes and use a simpler version of them in the business logic:这样做的目的是对数据类型进行抽象并在业务逻辑中使用它们的更简单版本:

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;
   }
}

The problem goes even deeper if I try to use IEnumerable instead of Collection:如果我尝试使用 IEnumerable 而不是 Collection,问题会更严重:

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

What am I missing and is there a workaround for this?我错过了什么,是否有解决方法?


EDIT:编辑:

I Made the IElement interface generic parameter covariant as follows:我使 IElement 接口泛型参数协变如下:

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

But this does not solve the interface implementation error.但这并不能解决接口实现错误。

The only way I can make the class to implement the interface correctly is by doing this:我可以使 class 正确实现接口的唯一方法是这样做:

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

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

Is there a limitation that causes the covariance to not be applied implicitly when implementing an interface?在实现接口时是否存在导致协方差不被隐式应用的限制?

Is there a more elegant way of doing this?有没有更优雅的方式来做到这一点?

I want to keep the IObject interface instead of using object directly because in the IObject interface if I ever need I can place a certain subset of the properties of the Neighbor class.我想保留IObject接口而不是直接使用object ,因为如果需要,我可以在IObject接口中放置Neighbor class 属性的某个子集。

Original scenario with Collection<T> Collection<T>的原始场景

You get this error, because your IElement<T> interface has a generic type parameter T and the property Neighbors is of type Collection<T> .您会收到此错误,因为您的IElement<T>接口具有泛型类型参数T并且属性Neighbors的类型为Collection<T> Hence, your Element type that implements IElement with the concrete type object needs to implement a Collection<object> not a Collection<Neighbor> .因此,使用具体类型object实现IElementElement类型需要实现Collection<object>而不是Collection<Neighbor> This property must be public , as it is implemented from your public interface and the default member visibility in a class is private .此属性必须是public的,因为它是从您的公共接口实现的,并且class中的默认成员可见性是private的。 The same applies if you replace object with Neighbor or any other type.如果将object替换为Neighbor或任何其他类型,这同样适用。 See this example for Neighbor .请参阅此示例Neighbor

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

While this resolves your original your error, you will not be able to pass instances of type Element to your method CanIDoSomething , because your type parameter T in IElement<T> is invariant , which means you can only use the originally specified type.虽然这解决了您最初的错误,但您将无法将Element类型的实例传递给您的方法CanIDoSomething ,因为IElement<T>中的类型参数Tinvariant ,这意味着您只能使用最初指定的类型。 In the example this type would be Neighbor and not IObject or object .在示例中,此类型为Neighbor而不是IObjectobject

You need to make yout type parameter covariant in order to use a more derived type like Neighbor .您需要使您的类型参数协变才能使用更派生的类型,例如Neighbor However, you will run into the issue that Collection<T> uses an invariant type parameter as well, so you will need to replace it with a type that has a covariant type parameter like IReadOnlyCollection<T> or IEnumerable<T> .但是,您会遇到Collection<T>也使用不变类型参数的问题,因此您需要将其替换为具有协变类型参数的类型,例如IReadOnlyCollection<T>IEnumerable<T> The example below will show you how convariance works for your case.下面的示例将向您展示协方差如何适用于您的案例。 You can start from there to use other types and collections.您可以从那里开始使用其他类型和 collections。

Scenario with generics and IEnumerable<T> generics 和IEnumerable<T>的场景

If you want your IElement<T> to be generic and use IEnumerable<T> , declare your interface with a convariant type parameter using the keyword out .如果您希望IElement<T>是通用的并使用IEnumerable<T> ,请使用关键字out使用可变类型参数声明您的接口。

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

Then implement it in your concrete class with the correct type, eg Neighbor .然后用正确的类型在你的具体 class 中实现它,例如Neighbor

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

You can keep both of your CanIDoSomething methods as they are, because Neighbor is derived from object and implements the IObject interface, so it will work with covariance in both cases.您可以保留两个CanIDoSomething方法,因为Neighbor派生自object并实现IObject接口,因此它在两种情况下都可以使用协方差。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM