list.ToArray vs list

本文关键字:list vs ToArray | 更新日期: 2023-09-27 18:02:28

我注意到一些奇怪的事情,有可能我错了。
我有接口IA和类A:

interface IA { .... }
class A : IA { .... }

在另一类中,我有这个:

private IList<A> AList;
public IList<IA> {
    get { return AList; }
}

但是我得到编译错误。但是如果我改成:

public IList<IA> {
    get { return AList.ToArray(); }
}

一切都好。

为什么?

list.ToArray vs list

为什么不行

private IList<A> AList;  
public IList<IA> { get { return AList; } } 

将属性暴露为IList<IA>将允许您尝试将class B : IA添加到列表中,但底层列表实际上是 IList<A>, B不是A,因此这将在您的脸上爆炸。因此,它是不允许的。

为什么工作:

public IList<IA> { get { return AList.ToArray(); } } 

数组方差被打破。您可以将列表作为数组返回,如果您尝试Add操作(或者尝试用A类型以外的对象替换给定索引处的对象),它仍然会在运行时出现问题,但它在编译时是合法的。这个差异的另一个例子:

string[] array = new string[10];
object[] objs = array; // legal
objs[0] = new Foo(); // will bite you at runtime 

From comments:

那你建议用什么呢?我怎样才能使物业申报表有效对象?我怎样才能使返回值只读?

如果消费者只需要迭代序列,而不是随机的,索引访问它,你可以公开属性作为IEnumerable<IA>

public IEnumerable<IA> TheList
{
     get { return AList.Select(a => a); }
}

(Select实际上在技术上并不需要,但是使用它将阻止消费者将结果强制转换为其真正的底层List<>类型)。如果消费者决定他们想要一个列表或数组,他们可以自由地调用ToList()ToArray(),并且无论他们对它做什么(就添加,删除,替换项而言)都不会影响列表。(对项目属性的更改将是可见的。)同样,您也可以自己以一种安全的方式公开IList<IA>集合

public IList<IA> TheList
{
    get { return AList.ToList<IA>(); }
}

同样,这将返回列表的副本,因此对它的任何更改都不会影响底层列表。

因为本地数组是坏的。这段代码很糟糕,你不应该这样做,c#的设计者迫切希望他们能撤销它。

数组是协变的,而列表不是。