使用基类作为 IEnumerable的泛型;

发布于 2024-12-16 13:55:50 字数 1330 浏览 0 评论 0原文

我对 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 转换为 IEnumerable< /代码>。

但是,我可以这样做:

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

Foo(new Entity());

还有这个:

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

CustomCollectionType<Entity> entityList;

Bar(entityList);

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

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

I have a good understanding of OOP in general, inheritance and polymorphism, interfaces, etc. I encountered a strange situation and I don't understand why it does not work at all...

EDIT : Ok, I found out that covariance (or contravariance?) may solve this problem, but crucially

we're still using .NET 2.0

How can I solve this without moving to C# 4.0 ?

Here is the situation. Given these two classes :

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

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

The compiler complains when I try to have this generic method

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

And try to use it this way :

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

LoopThrough(entityList);

Error says I cannot convert from CustomCollectionType<Entity> to IEnumerable<EntityBase>.

However, I can do this :

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

Foo(new Entity());

And this :

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

CustomCollectionType<Entity> entityList;

Bar(entityList);

Why can't I create my method with the highest classes in the hierarchy? The types are obviously compatible... Am I missing something ?

EDIT : I want to solve this problem without altering the existing classes in any way, so creating a new method in any of the classes, or implementing an additional interface is out of the question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

携余温的黄昏 2024-12-23 13:55:50

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

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

并且你调用

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

This failed in C# 3.0;它在 C# 4.0 中成功,因为 IEnumerable 现在在 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());

并且你只需将一个橙子放入只能容纳苹果的碗中。

Let's consider your first case. You have:

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

and you call

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

This fails in C# 3.0; it succeeds in C# 4.0 because IEnumerable<T> is now covariant in T; a sequence of apples can be used as a sequence of fruits.

To make it work in C# 3.0 you can use the Cast sequence operator.

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

To make it work in C# 2.0, implement the Cast sequence operator yourself. It is only a couple lines of code.

Note that in C# 4.0 it will still not be legal to say:

Bowl<Fruit> fruits = new Bowl<Apples>();

because of course you can say:

fruits.Add(new Orange());

and you just put an orange into a bowl that can only contain apples.

原来是傀儡 2024-12-23 13:55:50

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

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

Yes, .NET can be kind of annoying that way as it can't cast all of your generic parameters in one shot. Instead, perhaps try a generic approach like this to alleviate the problem.

public void LoopThrough<T>(IEnumerable<T> entityList) where T : EntityBase
{
    foreach(T entity in entityList) 
    {
        DoSomething(entity as EntityBase);  
    }
}
淡水深流 2024-12-23 13:55:50

类型是兼容的,但有点不兼容,这里的主要原因是您在参数中使用基类型作为 IEnumerable 而不是实际类型,尽管实体的基是实体基,因为类型参数和约束的规则对泛型类行为有几个影响,特别是关于继承和成员可访问性

泛型类是不变的。换句话说,如果输入参数指定 List 如果您尝试提供 List,则会收到编译时错误。这

就是为什么你会得到这个错误,就像你最后一个例子中的 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);  

因为实体列表具有 IEntityBase 类型,

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

The types are compatible but sort of uncompatible, the major reason here is that you are using the base type in parameter as IEnumerable and not the actual type although Entity's base is entitybase because the rules for type parameters and constraints have several implications for generic class behavior, especially regarding inheritance and member accessibility

Generic classes are invariant. In other words, if an input parameter specifies a List<BaseClass>, you will get a compile-time error if you try to provide a List<DerivedClass>.

And thats why you get that error where as in your last e.g. the T is same.

However it would have worked absolutely fine had you used interfaces because all interfaces are compatible

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

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

and than your method will work fine

CustomCollectionType<Entity> entityList;      LoopThrough(entityList);  

because entitylist has a type of IEntityBase

The other thing you can try is typeof(to get type) or using a cast and it should work

痕至 2024-12-23 13:55:50

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

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

那么您的 LoopThrough 应该工作,因为自定义集合类型是从 EntityBase 派生的,并且具有任何预期的方法、属性等可用...或者最坏的情况,您将在调用函数时对其进行类型转换,例如

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

I might be missing something, but if your intent of CustomCollectionType is supposed to be of an Entity base yet allowed to use the IEnumerable, shouldn't you have IT as a base of Entity base first? such as...

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

Then your LoopThrough SHOULD work as the custom collection type is derived FROM the EntityBase and have whatever expected methods, properties, etc available... or worst case, you would type-cast it when calling the function such as

Bowl<Apple> apples = whatever;
LoopThrough((EntityBase)apples);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文