我正在查看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?
评论突出显示的其他条件。
为什么分配了一个数组,将list2
中的元素复制到该临时数组中,然后将这些元素从该临时数组复制到list1
的末尾?为什么额外的副本?为什么不直接使用list2
方法将元素从list1
复制到ICollection<T>.CopyTo()
的末尾?
我会恭敬地提出,虽然这个问题受到我们这些没有编写代码的人的推测,但仍然有一个问题的答案。代码的编写方式是有原因的,我希望有知识的人可以提供解释,即使只是出于历史目的。
正如@ OlivierJacot-Descombes的评论部分所述,有关的额外副本已在.NET核库(CoreFX)的当前版本的List.cs中删除,并被单拷贝版本(即c.CopyTo(_items, index);
)取代。
感谢@elgonzo和@IvanStoev提供发人深省的评论。总而言之,这可能只是List<T>
演变的一个人工制品,并且代表了它开发时的最佳选择,但这只是猜测。