List<T>.AddRange / InsertRange 创建临时数组。

5

当我查看List.AddRange的实现时,发现了一些奇怪的东西,我无法理解。 源代码,见第727行(AddRange调用InsertRange)

T[] itemsToInsert = new T[count];
c.CopyTo(itemsToInsert, 0);
itemsToInsert.CopyTo(_items, index); 

为什么它首先将集合复制到“临时数组”(itemsToInsert)中,然后再将该临时数组复制到实际的_items数组中? 这是有原因的吗,还是只是从ArrayList源代码中复制过来的剩余部分,因为在那里也发生了同样的事情。

2
我会假设这是通过按值而不是按引用传递项目,以便在列表中的项目本身被修改的情况下不会修改插入的原始项目。 - JD Davis
1
@Jdsfighter - 我不明白那有什么帮助。数组或集合的内容要么是值,要么是引用。上面的代码对被复制的内容的本质没有任何改变。 - Damien_The_Unbeliever
我并不认为这是为了隐藏底层数组。在我的回答中,我指出了其他可能的解释。 - Fabjan
1个回答

4
我的猜测是这样做是为了隐藏内部支持数组的存在。故意没有办法获取对该数组的引用。即使是 List 类也不保证有这样的数组。(当然,出于性能和兼容性的原因,它总是使用数组实现。)
有人可能会传入一个精心构造的 ICollection<T>,它记住了传递给它的数组。现在调用者可以操纵 List 的内部数组并开始依赖于 List 内部。
与之形成对比的是 MemoryStream,它有一种记录方式来访问内部缓冲区(并用它射击自己):GetBuffer()

如果这是实际原因,那我真的看不出有什么意义:如果有人想要搞事情,他们也可以使用反射来查找私有数组字段。 - Pieter Witvoet
使用反射可以打开任何东西,但在实践中这更加困难且更少发生。这似乎是防止意外或粗心的API误用的保护措施。调用代码不可避免地开始依赖于当前确实为真但不能保证的不变量。这越难做,就越少发生。 - usr
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Pieter Witvoet
我理解你的观点。最容易出错的地方可能是在for循环中,像这样做target[i+index] = myValue[i]... - CSharpie
同时,它通过支持的手段关闭了访问内部数组的唯一间隙。InsertRange与集合的使用API很少。出于某种原因,我发现自己很少操作列表,除了调用Add方法。 - usr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接