Почему дополнительная копия в списке <T>.AddRange (IEnumerable <T>)? - PullRequest
0 голосов
/ 22 февраля 2019

Я смотрю на открытый исходный код для System.Collections.Generic.List .Метод 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, используя метод ICollection<T>.CopyTo()?


РЕДАКТИРОВАТЬ

Я бы с уважением заявил, что пока вопросПри условии спекуляции со стороны тех из нас, кто не написал код для начала, тем не менее, является ответом на вопрос.Есть причина, по которой код был написан таким, каким он был, и я надеялся, что кто-то, обладающий такими знаниями, сможет дать объяснение, даже если только для исторических целей.

1 Ответ

0 голосов
/ 23 февраля 2019

Как отмечено в разделе комментариев @ OlivierJacot-Descombes, дополнительная копия, о которой идет речь, была удалена в текущей версии List.cs в .NET Core Libraries (CoreFX) и заменена наверсия в единственном экземпляре, т.е. c.CopyTo(_items, index);.

Спасибо @elgonzo и @IvanStoev за предоставленные заставляющие думать комментарии.Подводя итог, можно сказать, что это просто артефакт эволюции List<T> и представляет собой лучший вариант на момент его разработки, но это всего лишь предположение.

...