0

对我来说,这样做是完美的:

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

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

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

4

5 回答 5

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项目列表输入的,所以很自然地,我们可以Ab.
  • 第 (2) 行有效,因为引用的实例b实际上是 的实例List<B>,所以强制转换是有效的。
  • 第 (3) 行将崩溃,因为b包含不属于 类型的项目Btmp这是 type的事实List<B>保证列表的所有项目都是 type ,但如果允许分配to B,情况就不再如此了。List<B>List<A>

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

于 2013-02-02T18:38:24.830 回答
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());

但是,如果您可以将 a 分配List<B>List<A>变量,则会产生类型错误,即使编译器知道该变量b是类型的List<A>,因此应该能够保存类型A对象。

相反,您只需使用new List<A>并向其添加类型的元素B,这是允许的,或者您将变量的类型更改为List<B>.

于 2013-02-02T18:49:09.643 回答
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> ...
于 2013-02-02T18:47:00.340 回答
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>期望 astring但得到object. 不好。

您可以使用前面提到的协变和逆变来使接口工作。你可以写一个这样的接口:

public interface Covariant<out T>
{
    T FunctionReturningT();
}

public class MyInterfaceImplementation<T> : Covariant<T>
{
    public T FunctionReturningT() { /* Blah Blah */ }
}

使接口covariant,这意味着它是合法的写:

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>();
于 2013-02-02T19:15:57.767 回答
0

这取决于泛型容器是否设置了协变逆变泛型参数。

您可以查看 MSDN 以了解它是如何声明的。

于 2013-02-02T18:37:54.010 回答