- PVSM.RU - https://www.pvsm.ru -

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

Новинки C# 7.2. Span<T> и Memory<T> - 1

Доброго времени суток, читатель! Столкнувшись с проблемой замедления работы системы при передачи использовании переменных хранимых в стеке я отправился в гугл и нашел отличное решение использование коллекции Span<T>, которая была добавлена в версии языка C# 7.2. Но я заметил, что в рунете почти нет статей посвященных этому обновлению. Поэтому я решил набросать небольшую статью, которая может быть полезна начинающим.

Добавлен новый модификатор доступа 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 = 0b1101_0111; // Раньше можно было только так.
int i = 0b_1101_0111; // Теперь стало немного красивее.

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

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

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

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

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

Добавлен модификатор 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> и используется для потокобезопасного доступа по ссылке к коллекции хранимой в стеке.

На этом я завершу свой небольшой обзор обновления. Надеюсь кому-нибудь пригодится данная информация. Я постарался описать изменения и нововведение языка наиболее простым языком. Спасибо за уделенное время.

Источник: Новые возможности C# 7.2 | Microsoft Docs [1]

Автор: shwan

Источник [2]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/c-2/276868

Ссылки в тексте:

[1] Новые возможности C# 7.2 | Microsoft Docs: https://docs.microsoft.com/ru-ru/dotnet/csharp/whats-new/csharp-7-2

[2] Источник: https://habrahabr.ru/post/352716/?utm_campaign=352716