使用基类作为 IEnumerable的泛型;
我对 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
让我们考虑您的第一个案例。你有:
并且你调用
This failed in C# 3.0;它在 C# 4.0 中成功,因为
IEnumerable
现在在 T 中是协变的;苹果序列可以用作水果序列。要使其在 C# 3.0 中工作,您可以使用
Cast
序列运算符。要使其在 C# 2.0 中工作,请自己实现 Cast 序列运算符。它只有几行代码。
请注意,在 C# 4.0 中,这样说: 仍然不合法,
因为你当然可以说:
并且你只需将一个橙子放入只能容纳苹果的碗中。
Let's consider your first case. You have:
and you call
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.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:
because of course you can say:
and you just put an orange into a bowl that can only contain apples.
是的,.NET 可能有点烦人,因为它无法一次性转换所有通用参数。相反,也许可以尝试像这样的通用方法来缓解问题。
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.
类型是兼容的,但有点不兼容,这里的主要原因是您在参数中使用基类型作为 IEnumerable 而不是实际类型,尽管实体的基是实体基,因为类型参数和约束的规则对泛型类行为有几个影响,特别是关于继承和成员可访问性
泛型类是不变的。换句话说,如果输入参数指定
List,
如果您尝试提供List
,则会收到编译时错误。这就是为什么你会得到这个错误,就像你最后一个例子中的 T 是一样的。
但是,如果您使用接口,它会工作得很好,因为所有接口都是兼容的
,并且您的方法会工作得很好,
因为实体列表具有 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 aList<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
and than your method will work fine
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
我可能会遗漏一些东西,但是如果您的 CustomCollectionType 意图应该是实体基础,但允许使用 IEnumerable,那么您不应该首先将 IT 作为实体基础的基础吗?例如...
那么您的 LoopThrough 应该工作,因为自定义集合类型是从 EntityBase 派生的,并且具有任何预期的方法、属性等可用...或者最坏的情况,您将在调用函数时对其进行类型转换,例如
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...
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