Implementacja listy <T> .AddRange jest nieoptymalna

Profilowanie mojej aplikacji C # wskazało, że spędzony jest znaczny czasList<T>.AddRange. Użycie Reflectora do obejrzenia kodu w tej metodzie wskazało, że wywołujeList<T>.InsertRange które jest realizowane jako takie:

public void InsertRange(int index, IEnumerable<T> collection)
{
    if (collection == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
    }
    if (index > this._size)
    {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
    }
    ICollection<T> is2 = collection as ICollection<T>;
    if (is2 != null)
    {
        int count = is2.Count;
        if (count > 0)
        {
            this.EnsureCapacity(this._size + count);
            if (index < this._size)
            {
                Array.Copy(this._items, index, this._items, index + count, this._size - index);
            }
            if (this == is2)
            {
                Array.Copy(this._items, 0, this._items, index, index);
                Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index));
            }
            else
            {
                T[] array = new T[count];          // (*)
                is2.CopyTo(array, 0);              // (*)
                array.CopyTo(this._items, index);  // (*)
            }
            this._size += count;
        }
    }
    else
    {
        using (IEnumerator<T> enumerator = collection.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                this.Insert(index++, enumerator.Current);
            }
        }
    }
    this._version++;
}

private T[] _items;

Można argumentować, że prostota interfejsu (mająca tylko jedno przeciążenie InsertRange) usprawiedliwia narzut wydajności związany z cheching i castingiem typu runtime. Ale co może być powodem 3 linii, które wskazałem(*) ? Myślę, że można go przepisać do szybszej alternatywy:

is2.CopyTo(this._items, index);

Czy widzisz jakiś powód, dla którego nie używasz tej prostszej i najwyraźniej szybszej alternatywy?

Edytować:

Dzięki za odpowiedzi. Tak więc zgodna opinia jest taka, że ​​jest to środek zabezpieczający przed zbiorem danych wejściowych implementujących CopyTo w sposób wadliwy / złośliwy. Dla mnie wydaje się przesadą, aby stale płacić cenę 1) sprawdzanie typu runtime 2) dynamiczną alokację tymczasowej tablicy 3) podwoić operację kopiowania, kiedy to wszystko mogło zostać zapisane przez zdefiniowanie 2 lub kilku dodatkowych przeciążeń InsertRange , jeden dostajeIEnumerable jak teraz, drugi dostajeList<T>, trzeci dostanieT[]. Późniejsze dwa mogły zostać zaimplementowane tak, aby działały dwa razy szybciej niż w bieżącym przypadku.

Edytuj 2:

Zaimplementowałem klasę FastList, identyczną z List, z tą różnicą, że zapewnia ona przeciążenie AddRange, która przyjmuje argument T []. To przeciążenie nie wymaga dynamicznej weryfikacji typu i podwójnego kopiowania elementów. Zrobiłem profil tego FastList.AddRange przeciwko List.AddRange, dodając tablice 4-bajtowe 1000 razy do listy, która początkowo była emtpy. Moja implementacja pokonuje prędkość standardowego List.AddRange ze współczynnikiem 9 (dziewięć!). List.AddRange zajmuje około 5% czasu wykonywania w jednym z ważnych scenariuszy użycia naszej aplikacji, zastępując List klasą zapewniającą szybszy AddRange może poprawić środowisko wykonawcze aplikacji o 4%.

questionAnswers(3)

yourAnswerToTheQuestion