对我而言,这样做是完美的:
public class A { }
public class B : A { }
public class C
{
public List<A> b = new List<B>();
}
List期望元素属于A类,对于List也是如此。我知道存在类型不匹配,但从逻辑上讲,编译器允许这种不匹配是完全合理的。为什么不呢?
答案 0 :(得分:1)
List<B>
无法分配给List<A>
,原因很充分。
让我们假设允许不匹配,让我们想象一下你的班级C
中的以下虚构方法:
public void DoSomething()
{
b.Add(new A()); // (1)
List<B> tmp = (List<B>)b; // (2)
foreach (B item in tmp) { // (3)
// ...
}
}
b
被列为A
项列表,所以很自然地,我们可以向A
添加新的b
实例。< / LI>
b
引用的实例实际上是List<B>
的实例,因此转换有效。b
包含不属于B
类型的项目。 tmp
类型为List<B>
的事实保证列表中的所有项都是B
类型,但如果将List<B>
分配给{{{},则不再是这种情况允许1}}。因此,编译器不允许这种不匹配。
答案 1 :(得分:0)
这取决于通用容器是否设置了 covariant 或 contravariant 泛型参数。
您可以查看MSDN以了解它的声明方式。
答案 2 :(得分:0)
你List<T>
中的T
是协变的,但事实并非如此。
在C#中,一些接口是协变的,但类不是。如上所述A
和B
,可以说是
IEnumerable<A> b = new List<B>();
或
IReadOnlyList<A> b = new List<B>();
因为有问题的接口是协变的,即在
中用“out”声明public interface IEnumerable<out T> ...
public interface IReadOnlyLies<out T> ...
答案 3 :(得分:0)
因为如果列表存储在List<A>
变量中,程序希望能够将A
类型的对象放在该变量中。在List<B>
变量中存储List<A>
对象会阻止您将A
类型的对象放入明确声明能够保存类型A
的列表中。
那是:
List<A> b = new List<B>()
// compiler knows list should be of type A, so it expects this to work:
b.add(new A());
但是,如果您可以将List<B>
分配给List<A>
变量,即使编译器知道变量b
,也会产生类型错误属于List<A>
类型,因此应该能够保存类型A
对象。
相反,您只需使用new List<A>
并向其添加B
类型的元素,这是允许的,或者您将变量的类型更改为List<B>
。
答案 4 :(得分:0)
使用类无法实现此目的。您可以使用界面(查找协方差和逆变),但不能使用List
/ IList
。想象一下以下场景:
public class MyClass<T>
{
T GetT() { /* Blah blah */ }
void SetT(T value) { /* Blah Blah */ }
}
写这个:
MyClass<object> example = new MyClass<string>();
这适用于第一种方法; example
应返回object
,MyClass<string>
返回一个字符串,由于多态性,这是合法的。
第二种方法更是一个问题。让我们说你之后写这个:
example.SetT(new object());
这是非法的,因为MyClass<string>
期望string
但获得object
。不好。
如前所述,您可以使用协方差和逆变使接口工作。您可以编写如下界面:
public interface Covariant<out T>
{
T FunctionReturningT();
}
public class MyInterfaceImplementation<T> : Covariant<T>
{
public T FunctionReturningT() { /* Blah Blah */ }
}
使界面协变,这意味着写入是合法的:
Covariant<object> example = new MyInterfaceImplementation<string>();
您还可以写下以下内容:
public interface Contravariant<in T>
{
void FunctionAskingForT(T value);
}
public class MyInterfaceImplementation<T> : Contravariant<T>
{
public void FunctionAskingForT(T value) { /* Blah Blah */ }
}
你刚刚创建了 contravariant 界面,这意味着写这个是合法的:
Contravariant<string> example = new MyInterfaceImplementation<object>();