假设我们有两个类A
和B
,其中B派生自A.
class A
{
}
class B : A
{
}
class C<T>
{
}
现在C<B>
未从C<A>
派生。
这让我有点困惑,因为我坚持用A做的“一切”,我可以做B. 我确信我错过了一些东西,因为它似乎与OOP的基础相矛盾。
是否有任何具体的理由和例子?
修改
我读过很多类似的帖子,比如: In C#, why can't a List<string> object be stored in a List<object> variable
Casting from IEnumerable<Object> to IEnumerable<string>
但在每篇文章中,答案都集中在特定的类/数据结构中,并使这个特定的代码工作,而不是为什么它一般不可能?
答案 0 :(得分:9)
虽然我可以用A做“一切”,我可以用B做。我确信我错过了一些东西,因为它似乎与OOP的基础相矛盾。
让我们先说明你提到的basic principle。来自维基百科:
如果S是T的子类型,那么类型T的对象可以用类型S的对象替换而不改变该程序的任何所需属性
您缺少的是关键词:对象。
您可以将类型为B
的对象替换为需要类型为A
的对象的任何位置。但是,使用类型 A
您可以使用类型 B
进行的所有事情都不是这样。如果有人问你要一辆车而你给他们一辆1995年的福特护航,那么他们就可以把它用作汽车。但这并不意味着您可以在维基百科中进行搜索和替换,并将“car”一词的每个用法改为“1995 Ford Escort”,并且文章仍然正确!
事实上,您无法使用C<B>
预期C<A>
的原因是因为违反了 Liskov替换原则:
class A {}
class B : A {}
class D : A {}
class C<T>
{
public T M(T t) { ... }
}
...
C<B> cb = new C<B>();
C<A> ca = cb; // Suppose this were legal
ca.M(new D());
您可以在任何可以使用D
的地方使用A
类型的对象,但ca
实际上是cb
,并且您无法使用D
你可以在任何地方使用B
!因此,您无法在任何可以使用C<B>
的地方使用C<A>
。
答案 1 :(得分:2)
看看这个例子:
class C<T>
{
public T Value { get; set; }
}
// ...
C<B> cb = new C<B>();
现在,如果C<B>
来自C<A>
,则可能会出现以下情况:
C<A> ca = cb;
ca.Value = new A(); // ca.Value is defined by C<A> and thus typed to A
想象一下当我们现在访问cb.Value
时会发生什么,假设存储的实例只能是B
类型,但它实际上是A
类型。
因此,C<B>
并非衍生自({+ 1}}不符合任何内容。
答案 2 :(得分:1)
好吧,当你写一个课程C<T>
时,所有内容都是C
,而不是T
。 T
仅仅是提供用于表示特殊C<>
类型的类型信息。 C<>
有自己的实现,与T
完全没有关系。当你写:
var c = new C<int>();
变量c
的行为完全符合您在课程C<>
中所写的内容。无论T
是int
还是string
还是其他任何内容,它的行为都相同。
另请注意,类似C<S>
的类是一种特殊类型,与C<T>
不同,后者是另一种类型,因为所有行为都受其C
限制。我们永远不知道C<>
是如何写的以及它将如何表现的。所以你所描述的方式不是泛型如何发挥作用。如果你有一个额外的类,如
class D<T> : C<T>
{
}
你可以写:
C<A> c = new D<A>();
你知道,这可能是因为C
来自D
。班级的左侧很重要。这也是非法的:
C<A> c = new D<B>();
即使A
来自B
,因为C
的{{1}}与A
的{{1}}无关(甚至D
1 {} B
)这是一个单独的类。
当您将C
视为与B
完全不同的类型时,这种通用多态实际上毫无意义,尽管它们具有共同的行为。为了说明另一个例子,考虑一下,一个类可以通过多个类型来通用。假设有
C<A>
C<B>
和class C<S, T>
{
}
怎么样?它们的类型是否具有可比性,因为这两个类型具有C<IList<A>, A>
和C<IList<int>, B>
以及IList
作为泛型类型?来自A
的{{1}}是否可投弃?简而言之,这些都是不同的类型。
现在,如果你不想做作业
B
可能你可以编写自己的隐式转换器。
C<T>
请注意,这并不意味着继承关系,而只是转换操作。
没有关于协方差和逆变的信息(仅适用于接口和代表),没有关于泛型的完整信息。如果发生这种情况,请参阅O. R. Mapper关于危险的答案。 C#允许这种情况的一种情况是数组。遗憾的是,这是一个破碎的设计。这在C#
中是可能的C<S, T>
我很想详细说明,但我做的任何事情都会让它变得多余。请参阅此优秀答案by Eric Lippert以获取明确信息。