我正在查看 System.Collections.Generic.List<T> 的开源代码. AddRange(IEnumerable<T>)
方法如下所示:
public void AddRange(IEnumerable<T> collection) {
Contract.Ensures(Count >= Contract.OldValue(Count));
InsertRange(_size, collection);
}
和 InsertRange(int, IEnumerable<T>)
方法看起来像这样:
public void InsertRange(int index, IEnumerable<T> collection) {
if (collection==null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
}
if ((uint)index > (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
}
Contract.EndContractBlock();
ICollection<T> c = collection as ICollection<T>;
if( c != null ) {
int count = c.Count;
if (count > 0) {
EnsureCapacity(_size + count);
if (index < _size) {
Array.Copy(_items, index, _items, index + count, _size - index);
}
if (this == c) {
Array.Copy(_items, 0, _items, index, index);
Array.Copy(_items, index+count, _items, index*2, _size-index);
}
else {
T[] itemsToInsert = new T[count]; // WHY?
c.CopyTo(itemsToInsert, 0); // WHY?
itemsToInsert.CopyTo(_items, index); // WHY?
// c.CopyTo(_items, index); // WHY NOT THIS INSTEAD???
}
_size += count;
}
}
else {
using(IEnumerator<T> en = collection.GetEnumerator()) {
while(en.MoveNext()) {
Insert(index++, en.Current);
}
}
}
_version++;
}
假设我们这样调用:
var list1 = new List<int> {0, 1, 2, 3};
var list2 = new List<int> {4, 5, 6, 7};
list1.AddRange(list2);
当它到达 InsertRange(int, IEnumerable<T>)
时在内部,它最终会遇到 // WHY?
突出显示的 else 条件评论。
为什么分配一个数组,元素来自list2
被复制到该临时数组中,然后元素从该临时数组复制到 list1
的末尾?为什么额外的副本?为什么不直接从 list2
复制元素呢?到 list1
结束使用 ICollection<T>.CopyTo()
方法?
编辑
我会恭敬地提出,虽然这个问题是由我们这些一开始没有编写代码的人猜测的,但仍然是这个问题的答案。代码以这种方式编写是有原因的,我希望有这方面知识的人可以提供解释,即使只是出于历史目的。
最佳答案
正如@OlivierJacot-Descombes 在评论部分指出的那样,当前版本的 List.cs 中删除了有问题的额外副本。在 .NET Core 库 (CoreFX) 中并替换为单一副本版本,即 c.CopyTo(_items, index);
.
感谢@elgonzo 和@IvanStoev 提供发人深省的评论。总结一下,这可能只是 List<T>
进化的产物。代表了开发时的最佳选择,但这只是一个猜测。
关于c# - 为什么要在 List<T>.AddRange(IEnumerable<T>) 中添加额外的副本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54834588/