Найти тему

Новинки C# 7.2. Span и Memory

Оглавление

Вышел новый релиз языка C# версии 7.2, в котором были реализованы новые возможности. По словам кампании Microsoft, одной из основных целей данного выпуска было увеличение эффективности работы с типами данных хранимых по значению (в стеке), путем ухода от избыточного выделения памяти и выполнения ненужных операций копирования. Давайте рассмотрим основные новинки языка.

Добавлен новый модификатор доступа private protected

Данный модификатор доступа позволяет обращаться только производным классам в пределах одной сборки. В отличие от реализованного ранее модификатора protected internal, который позволял обращаться как производным классам, так всем и классам этой же сборки.

Неконечные именованные аргументы

В C# могут использоваться так называемые именованные аргументы. Они позволяют разработчику не запоминать необходимый порядок следования параметров метода, если вручную указывается имя параметра. Давайте рассмотрим сигнатуру данного метода:

public void Print(string text, int size, string title)

Мы можем обратиться к этому методу как обычно

Print("Hello, World!", 24, "Hi!");

А можем воспользоваться именованными аргументами и задавать параметры в произвольном порядке. Или задать часть аргументов по порядку (такие аргументы называются позиционные), а оставшиеся задать с помощью имени.

Print(size: 24, title: "Hi!", text: "Hello, World!");
Print("Hello, World!", title: "Hi!", size: 24);

Так вот, раньше было ограничение, что после именованных аргументов могли идти только другие именованные аргументы. Если после именованного аргумента был указан позиционный, то выдавалось сообщение об ошибке.

Теперь же, если аргумент находится в правильной позиции, после именованного аргумента, то программа будет работать корректно.

Print(text: "Hello, World!", 24, "Hi!");

Но при этом, если позиционный порядок был нарушен, то будет выведено сообщение об ошибке. Данные код работать не будет, так как не смотря на то, что size находится на месте, title и text поменяны местами.

Print(title: "Hi!", 24, text: "Hello, World!"); // Ошибка.

Начальные символы подчеркивания в числовых литералах

В языке C# уже была реализована возможность использовать двоичные и шестнадцатеричные литералы с использованием разделителя подчеркивание «_», поэтому ранее использование его в качестве первого символа литерала было запрещено. Теперь это ограничение было снято.

int i= 0b_1101_0111;

Использование ссылок для типов данных хранимых по значению

Для начала стоит вспомнить, что существуют два типа данных:

  • Хранимые по значению — простые типы данных, такие как int или bool, которые хранятся напрямую в стеке, за счет чего к ним осуществляется быстрый доступ
  • Хранимые по ссылке — более сложные структуры, такие как string или классы, которые хранятся в куче, а в стеке хранится только указатель на область памяти кучи.

При этом если значимый тип передавался в метод, то создавалась его новая копия, поэтому изменить изначальную переменную внутри метода было невозможно, если не использовались модификаторы доступа (ref). Но если в метод передавался ссылочный тип, то копирование объекта не выполнялось, и изменение объекта в методе изменяло изначальный объект.

Здесь заключается одна не очевидная проблема, так как при передаче значимого типа в метод каждый раз создается его новая копия, то возникает замедление работы системы и чрезмерное использование памяти. Особенно это актуально для объемных структур хранимых в стеке,  например массивов. Представьте себе рекурсивный вызов метода, с передачей в качестве аргумента массива из 1 000 000 элементов… Поэтому в новой версии языка были предприняты шаги для оптимизации работы с переменными хранимыми по значению.

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

private int Sum(in int value1, in int value2);

Так как структура struct тоже хранится в стеке, для нее добавлено использование модификатор readonly, который по сути является аналогом in

readonly public struct ReadonlyPosition
{
public ReadonlyPosition(int x, int y)
{
X = x;
Y = y;
}

public int X { get; }
public int Y { get; }

private static readonly ReadonlyPosition _position = new ReadonlyPosition();
public static ref ReadonlyPosition Position => ref _position;
}

Добавлен модификатор ref struct, который указывает, что структура передается по ссылке, и должен обрабатываться с выделением стека.

Также Добавлен модификатор доступа ref readonly, который указывает, что значение возвращается по ссылке, но при этом запрещено изменение соответствующей переменной.

Ну и наконец был добавлен тип Span<T>, который позволяет создать коллекцию данных, хранимую в стеке, но доступ к которой осуществляется по ссылке. Тип Memory<T> является расширением типа Span<T> и используется для потокобезопасного доступа по ссылке к коллекции хранимой в стеке.

Надеюсь данная статья была полезной и интересной.

Источник: https://shwanoff.ru/new-c-7-2/