Как новый С# Span <t> отличается от ArraySegment <t> ?

У меня возникли проблемы с концептуализацией использования нового Span в С#.

  1. Какую конструкцию он заменяет? Является ли ArraySegment устаревшим?

  2. Какая функциональность это позволяет, что раньше не было?

  3. Является ли Span действительной заменой для массивов С#? В каких случаях да, в каких случаях нет?

  4. Когда я буду использовать ArraySegment вместо Span?

Я пытаюсь понять, как изменить стили кодирования, чтобы эффективно использовать новый Span.

Ответ 1

Span<T> ничего не заменяет. Это добавленная стоимость. Он обеспечивает безопасный вид в непрерывные сегменты памяти, которые могут быть распределены по-разному: либо в виде управляемого массива, либо в стеке -based, либо в неуправляемой памяти.

ArraySegment<T> ограничивается управляемыми массивами. Вы не можете использовать его для переноса данных, выделенных в стеке, с помощью stackalloc. Span<T> позволяет это сделать.

ArraySegment<T> также не предоставляет просмотр только для чтения в базовый массив. ReadOnlySpan<T> дает вам это.

Span<T> не должен заменять массивы. В конце концов, это просто взгляд на данные. Эти данные должны быть каким-то образом выделены, а в управляемом мире это распределение, в большинстве случаев, будет распределением массива. Поэтому вам все еще нужны массивы.

Вы должны использовать Span<T> если вы хотите, чтобы ваш код мог манипулировать больше, чем просто массивы. Например, рассмотрим библиотеку разбора. Прямо сейчас, чтобы позволить ему работать с массивами, выделять стек памяти и неуправляемую память, он должен предоставить несколько точек входа в API для каждого из них и использовать небезопасный код для фактического управления данными. Также, вероятно, необходимо будет выставить API-интерфейс string -based, который будет использоваться людьми, у которых их данные распределены как строки. С помощью Span и ReadOnlySpan вы можете объединить всю эту логику с одним решением Span -based, которое будет применяться во всех этих сценариях.

Span<T> определенно не будет тем, что используется всеми и очень часто. Это высокоспециализированная часть платформы.NET, которая в значительной степени полезна для авторов библиотек и критически важных сценариев с высокой производительностью. Например, Kestrel, веб-сервис за ASP.NET Core получит много преимуществ от перехода на Span<T> потому что, например, синтаксический анализ запроса может быть выполнен с использованием Span<T> и выделенной стеком памяти, что не оказывает никакого давления на GC, Но вам, написав веб-сайты и службы на базе ASP.NET Core, не нужно его использовать.

Ответ 2

Из журнала MSDN: Span определяется таким образом, что операции могут быть такими же эффективными, как и на массивах: индексирование в span не требует вычисления для определения начала от указателя и его начального смещения, поскольку само поле ref уже инкапсулирует оба. (В отличие от этого, ArraySegment имеет отдельное поле смещения, что делает его более дорогостоящим как для индексации, так и для прохождения.)

Кроме того, хотя Array реализует IEnumerable, Span этого не делает.

Ответ 3

Примите также во внимание, решая, использовать ли Span ограничения, которые применялись к ссылочным структурам в С#:

https://docs.microsoft.com/en-us/dotnet/api/system.span-1?view=netcore-2.2

Span - это структура ref, которая размещается в стеке, а не в управляемой куче. Типы ref структуры имеют ряд ограничений, чтобы гарантировать, что они не могут быть перенесены в управляемую кучу, включая то, что они не могут быть упакованы, они не могут быть назначены переменным типа Object, динамическим или любому типу интерфейса, они могут не могут быть полями в ссылочном типе, и они не могут использоваться через границы ожидания и выхода. Кроме того, вызовы двух методов, Equals (Object) и GetHashCode, генерируют исключение NotSupportedException.

Важный

Поскольку это тип только для стека, Span не подходит для многих сценариев, которые требуют хранения ссылок на буферы в куче. Это относится, например, к подпрограммам, которые выполняют асинхронные вызовы методов. Для таких сценариев вы можете использовать бесплатные типы System.Memory и System.ReadOnlyMemory.

Для диапазонов, представляющих неизменяемые или доступные только для чтения структуры, используйте System.ReadOnlySpan.

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref?view=netcore-2.2#ref-struct-types

Добавление модификатора ref в объявление структуры определяет, что экземпляры этого типа должны быть выделены в стеке. Другими словами, экземпляры этих типов никогда не могут быть созданы в куче как член другого класса. Основной мотивацией для этой функции был Span и связанные с ним структуры.

Цель сохранить тип структуры ref как переменную, выделенную из стека, вводит несколько правил, которые компилятор применяет для всех типов структуры ref.

  • Вы не можете боксировать ref структуру.
  • Вы не можете назначить тип ref структуры переменной объекта типа, динамического или любого типа интерфейса.
  • Типы ref структуры не могут реализовывать интерфейсы.
  • Вы не можете объявить ref struct как член класса или обычную структуру.
  • Вы не можете объявлять локальные переменные типа ref struct в асинхронных методах. Вы можете объявить их в синхронных методах, которые возвращают Task, Task или Task-like типы.
  • Вы не можете объявить локальные переменные ref struct в итераторах.
  • Вы не можете захватывать переменные ref struct в лямбда-выражениях или локальных функциях.
  • Эти ограничения гарантируют, что вы не будете случайно использовать структуру ref таким образом, чтобы она могла быть преобразована в управляемую кучу.

Вы можете комбинировать модификаторы, чтобы объявить структуру только для чтения ref. Только для чтения ref структура объединяет преимущества и ограничения объявлений ref struct и readonly struct.