具有继承的模板类参数的集合

时间:2013-02-02 18:31:54

标签: c# templates inheritance

对我而言,这样做是完美的:

public class A { }
public class B : A { }

public class C
{
    public List<A> b = new List<B>();
}

List期望元素属于A类,对于List也是如此。我知道存在类型不匹配,但从逻辑上讲,编译器允许这种不匹配是完全合理的。为什么不呢?

5 个答案:

答案 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)
        // ...
    }
}
  • 第(1)行有效,因为b被列为A项列表,所以很自然地,我们可以向A添加新的b实例。< / LI>
  • 第(2)行有效,因为b引用的实例实际上是List<B>的实例,因此转换有效。
  • 第(3)行会崩溃,因为b包含不属于B类型的项目。 tmp类型为List<B>的事实保证列表中的所有项都是B类型,但如果将List<B>分配给{{{},则不再是这种情况允许1}}。

因此,编译器不允许这种不匹配。

答案 1 :(得分:0)

这取决于通用容器是否设置了 covariant contravariant 泛型参数。

您可以查看MSDN以了解它的声明方式。

答案 2 :(得分:0)

List<T>中的T是协变的,但事实并非如此。

在C#中,一些接口是协变的,但类不是。如上所述AB,可以说是

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应返回objectMyClass<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>();