Как новый С# Span <t> отличается от ArraySegment <t> ?
У меня возникли проблемы с концептуализацией использования нового Span в С#.
-
Какую конструкцию он заменяет? Является ли ArraySegment устаревшим?
-
Какая функциональность это позволяет, что раньше не было?
-
Является ли Span действительной заменой для массивов С#? В каких случаях да, в каких случаях нет?
-
Когда я буду использовать 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.