Posted on 7. June 2021

.NET 6: Улучшения коллекций

 

Емкость списка, стека и очереди

Перед выполнением большого набора вставок в Dictionary или HashSet полезно вызвать EnsureCapacity с ожидаемым размером коллекции. Это позволяет коллекции выполнить одну операцию изменения размера заранее, избегая необходимости в нескольких изменениях размера.

Метод EnsureCapacity был добавлен в классы ListStack и Queue, чтобы они тоже могли повысить производительность.

Заметным исключением из этой группы является Коллекция . В отличие от других, Collection может при желании обернуть другую коллекцию, которая не обязательно может предоставлять метод EnsureCapacity. Как подкласс CollectionObservableCollection также не может предоставлять метод EnsureCapacity.

Это не единственный недостаток дизайна в Collection и ObservableCollection. Отсутствие метода AddRange давно раздражало разработчиков. Ему также не хватает высокопроизводительного IEnumerator на основе структур, который предлагает List<T>. Так что, возможно, пришло время предложить новую альтернативу, в которой нет конструктора-оболочки IList<T> и связанных с ним проблем.

Изменяемые структуры и словари

Хотя это случается редко, разработчикам иногда приходится работать с изменяемыми структурами. Это может быть сложно, потому что легко случайно скопировать изменяемую структуру, в результате чего два значения больше не будут синхронизироваться.

Чтобы решить эту проблему, нужно намеренно скопировать структуру, внести изменения, а затем скопировать ее обратно в исходное место. Для небольших конструкций это неплохо, но для больших может быть дорого. А так как производительность часто упоминается как причина использования изменяемых структур в первую очередь, использование copy-out / copy-in контрпродуктивно.

Чтобы избежать этих ненужных копий, изменяемые структуры обычно хранились в массивах. В отличие от свойства индексатора в List<T>, доступ к элементам массива осуществляется напрямую.

В C #7 были введены ссылочные возвращаемые значения (ref return). Это позволило индексаторам возвращать ссылку на объект, а не на копию.

public ref int this[int index]

{

    get { return ref _internalArray[index]; }

}

Этот метод используется структурой Span , начиная с .NET Core 2.1. Начиная с .NET 5, метод CollectionsMarshal.AsSpan позволяет легко получить оболочку диапазона вокруг коллекции.

Чтобы предложить те же возможности для словарей, была создана новая функция под названием CollectionsMarshal.GetValueRefOrNullRef. Есть несколько интересных фактов об этой функции.

Во-первых, это не метод расширения. Разработчики опасались, что неправильно использовать его будет слишком просто, поэтому они намеренно хотели, чтобы его было сложно найти. (Функцию AsSpan также потенциально небезопасно использовать, поскольку размеры коллекции не могут быть изменены, пока существует Span<T>)

Во-вторых, функция уже существует как внутренний метод Dictionary<TKey, TValue>.FindValue. Они просто раскрывают его через GetValueRefOrNullRef.

public static ref TValue GetValueRefOrNullRef<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key) where TKey : notnull

     => ref dictionary.FindValue(key);

 

Сам FindValue использует некоторые методы, которые не распространены в программировании на C#. Вот последние несколько строк, в которых вы можете найти широкое использование операторов goto, включая переход назад с переходом на goto.

   // The chain of entries forms a loop; which means a concurrent update has happened.

            // Break out of the loop and throw, rather than looping forever.

            goto ConcurrentOperation;

        }

    }

 

    goto ReturnNotFound;

 

ConcurrentOperation:

    ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();

ReturnFound:

    ref TValue value = ref entry.value;

Return:

    return ref value;

ReturnNotFound:

    value = ref Unsafe.NullRef<TValue>();

    goto Return;

}

 

После вызова GetValueRefOrNullRef нужно использовать CompilerServices.Unsafe.IsNullRef, чтобы увидеть, является ли результат ссылкой на фактическое значение или на нуль. Это связано с тем, что в C# нет синтаксиса для проверки того, является ли ссылка на структуру нулевой.

Serialization Enhancements for Dictionaries

 

Источник

 



Comments are closed