为什么要在List <t> .AddRange(IEnumerable <t>)中进行额外复制?

时间:2019-02-22 20:18:07

标签: c# .net performance generics generic-list

我正在寻找System.Collections.Generic.List<T>的开源代码。 dplyr [1,] "dplyr" [2,] "stats" [3,] "graphics" [4,] "grDevices" [5,] "utils" [6,] "datasets" [7,] "methods" [8,] "base" 方法如下:

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>)注释突出显示的else条件。

为什么要分配一个数组,将// WHY?中的元素复制到该临时数组中,然后将元素从该临时数组中复制到list2的末尾?为什么要额外复制?为什么不使用list1方法将元素从list2直接复制到list1的末尾?


编辑

我谨谨此提出,虽然这个问题是由我们这些没有写代码开始的人来推测的,但是一个问题的答案。编写代码的方式是有原因的,我希望有知识的人可以提供解释,即使只是出于历史目的。

1 个答案:

答案 0 :(得分:1)

如@ OlivierJacot-Descombes的注释部分所指出的那样,有问题的多余副本已在.NET Core库(CoreFX)的当前List.cs版本中删除,并由单副本版本取代,即c.CopyTo(_items, index);

感谢@elgonzo和@IvanStoev提供了发人深省的评论。总结一下,这可能只是List<T>演变的产物,它代表了@Entity @Table(name ="cats") public class Cat { @Id @Column(name="name") private String name; @Column(name="age") private int age; @Column(name="color") private String color; @Column(name="weight") private int weigth; .. } 开发时的最佳选择,但这只是一个猜测。