虽然可以从基类/接口继承,但为什么不能声明List<>呢 使用相同的类/接口?

interface A
{ }

class B : A
{ }

class C : B
{ }

class Test
{
    static void Main(string[] args)
    {
        A a = new C(); // OK
        List<A> listOfA = new List<C>(); // compiler Error
    }
}

有别的办法吗?


当前回答

如果你使用IEnumerable代替,它将工作(至少在c# 4.0中,我没有尝试过以前的版本)。这只是一个集合,当然,它仍然是一个列表。

而不是——

List<A> listOfA = new List<C>(); // 编译器错误

在原代码的问题中,使用-

IEnumerable<A> listOfA = new List<C>();//编译错误-没有更多!:)

其他回答

你也可以使用System.Runtime.CompilerServices.Unsafe NuGet包来创建同一个列表的引用:

using System.Runtime.CompilerServices;
...
class Tool { }
class Hammer : Tool { }
...
var hammers = new List<Hammer>();
...
var tools = Unsafe.As<List<Tool>>(hammers);

对于上面的示例,您可以使用tools变量访问列表中现有的Hammer实例。将Tool实例添加到列表中会抛出ArrayTypeMismatchException异常,因为tools引用了与hammers相同的变量。

只能强制转换为只读列表。例如:

IEnumerable<A> enumOfA = new List<C>();//This works
IReadOnlyCollection<A> ro_colOfA = new List<C>();//This works
IReadOnlyList<A> ro_listOfA = new List<C>();//This works

对于支持保存元素的列表,不能这样做。原因是:

List<string> listString=new List<string>();
List<object> listObject=(List<object>)listString;//Assume that this is possible
listObject.Add(new object());

现在该做什么?记住listObject和listString实际上是同一个列表,所以listString现在有object元素,这应该是不可能的。

首先,停止使用难以理解的类名,如A, B, c,使用动物,哺乳动物,长颈鹿,或食物,水果,橙子或其他关系清楚的东西。

你的问题是"既然我可以把长颈鹿赋值给动物类型的变量,为什么我不能把长颈鹿的列表赋值给动物类型的变量?"

答案是:假设你可以。那么会出什么问题呢?

嗯,你可以在动物列表中添加一只老虎。假设我们允许您将一个长颈鹿列表放入一个包含动物列表的变量中。然后你试着在名单上加上一只老虎。会发生什么呢?你想在长颈鹿的名单中包含一只老虎吗?你想撞车吗?或者你希望编译器通过在第一时间使赋值非法来保护你不崩溃?

我们选择后者。

这种转换称为“协变”转换。在c# 4中,我们将允许您在接口和委托上进行协变转换,前提是这种转换总是安全的。详见我关于协方差和逆变的博客文章。(这周的周一和周四会有一个新的话题。)

因为c#目前不允许这种类型的继承转换。

如果你使用IEnumerable代替,它将工作(至少在c# 4.0中,我没有尝试过以前的版本)。这只是一个集合,当然,它仍然是一个列表。

而不是——

List<A> listOfA = new List<C>(); // 编译器错误

在原代码的问题中,使用-

IEnumerable<A> listOfA = new List<C>();//编译错误-没有更多!:)