使用基类作为IEnumerable <t> </t>的通用

时间:2011-11-17 17:25:57

标签: c# generics inheritance c#-2.0 covariance

我对OOP一般,继承和多态,接口等有很好的理解。我遇到了一个奇怪的情况,我不明白为什么它根本不起作用...

编辑:好的,我发现协方差(或逆变?)可以解决这个问题,但至关重要

我们仍在使用.NET 2.0

如何在不迁移到C#4.0的情况下解决这个问题?

情况如下。鉴于这两个类:

public class CustomCollectionType<T> : IEnumerable<T>
{
    /* Implementation here, not really important */
}

public class Entity : EntityBase
{
    /* Implentation here, not important */
}

当我尝试使用这种通用方法时,编译器会抱怨

public void LoopThrough(IEnumerable<EntityBase> entityList)
{
    foreach(EntityBase entity in entityList) 
    {
        DoSomething(entity);  
    }
}

尝试以这种方式使用它:

CustomCollectionType<Entity> entityList;
/* Add items to list */

LoopThrough(entityList);

错误说我无法从CustomCollectionType<Entity>转换为IEnumerable<EntityBase>

但是,我可以这样做:

public void Foo(EntityBase entity)
{
    entity.DoSomething();
}

Foo(new Entity());

而且:

public void Bar(IEnumerable<Entity> entityList)
{ ... }

CustomCollectionType<Entity> entityList;

Bar(entityList);

为什么我不能用层次结构中最高的类创建我的方法?这些类型显然是兼容的...我错过了什么吗?

编辑:我想在不改变现有类的情况下解决这个问题,因此在任何类中创建一个新方法,或者实现一个额外的接口都是不可能的。

4 个答案:

答案 0 :(得分:15)

让我们考虑你的第一个案例。你有:

class Bowl<T> : IEnumerable<T> {}
class Apple : Fruit {}
...
void LoopThrough(IEnumerable<Fruit> fruits) ...

你打电话

Bowl<Apple> apples = whatever;
LoopThrough(apples);

这在C#3.0中失败了;它在C#4.0中取得成功,因为IEnumerable<T>现在在T中是协变的;一系列苹果可以用作一系列水果。

要使其在C#3.0中运行,您可以使用Cast序列运算符。

Bowl<Apple> apples = whatever;
LoopThrough(apples.Cast<Fruit>());

要使其在C#2.0中运行,自己实现Cast序列运算符。它只有几行代码。

请注意,在C#4.0中,说:

仍然是不合法的
Bowl<Fruit> fruits = new Bowl<Apples>();

因为你当然可以说:

fruits.Add(new Orange());

你只需将橙子放入一个只能装苹果的碗里。

答案 1 :(得分:4)

是的,.NET可能会有点烦人,因为它无法一次性投射所有通用参数。相反,也许尝试这样的通用方法来缓解问题。

public void LoopThrough<T>(IEnumerable<T> entityList) where T : EntityBase
{
    foreach(T entity in entityList) 
    {
        DoSomething(entity as EntityBase);  
    }
}

答案 2 :(得分:0)

这些类型兼容但有点不兼容,这里的主要原因是你在参数中使用基类型作为IEnumerable而不是实际类型,尽管实体的基础是实体库,因为类型参数和约束的规则有几个含义泛型类行为,尤其是有关继承和成员可访问性的行为

通用类是不变的。换句话说,如果输入参数指定List<BaseClass>,,如果您尝试提供List<DerivedClass>,则会出现编译时错误。

这就是为什么你会在最后一次出现错误的原因T是一样的。

然而,如果您使用接口,那么它将完全正常工作,因为所有接口都是兼容的

  public class Entity : IEntityBase  
  {      /* Implentation here, not important */  }  

public void LoopThrough(IEnumerable<IEntityBase> entityList)  
 {      foreach(IEntityBase entity in entityList)       
     {          DoSomething(entity);        }  }  

并且你的方法可以正常工作

CustomCollectionType<Entity> entityList;      LoopThrough(entityList);  

因为entitylist有一种IEntityBase

你可以尝试的另一件事是typeof(获取类型)或使用强制转换它应该可以工作

答案 3 :(得分:0)

我可能会遗漏一些东西,但是如果您的CustomCollectionType的意图应该是实体基础但允许使用IEnumerable,那么您是否应该首先将IT作为实体库的基础?比如......

public class CustomCollectionType<T> : EntityBase, IEnumerable<T>
{
    /* Implementation here, not really important */
}

然后你的LoopThrough应该工作,因为自定义集合类型是从EntityBase派生出来的,并且有任何预期的方法,属性等可用...或者最坏的情况,你可以在调用函数时输入它,例如

Bowl<Apple> apples = whatever;
LoopThrough((EntityBase)apples);
相关问题