Что нового появилось в C# 7 и уже поддерживается в Visual Studio “15” Preview 4
Сейчас использовать out переменные не так легко, как нам хотелось бы. Перед тем как вызвать метод с out аргументами, необходимо объявить переменные, которые будут переданы в этот метод. Так как обычно значения этим переменным во время объявления не присваиваются (что логично — они все равно будут перезаписаны методом), ключевое слово var использовать не получится. Надо объявлять переменные с указанием их типа:
В C# 7 добавлены out переменные, которые позволяют объявить переменные сразу в вызове метода:
Область видимости для таких переменных является внешний блок, именно поэтому можно их использовать в следующем за вызовом метода выражении.
Внимание: В Preview 4 действуют более строгие ограничения на область видимости: out переменные можно использовать только внутри выражения, где они были определены. Поэтому для того, чтобы вышеуказанный пример заработал, надо дождаться следующего релиза.
Так как объявление out переменных происходит в том же выражении, что и их передача в качестве аргументов метода, компилятор может вывести их тип (если для этого метода нет конфликтующих перегрузок), поэтому вместо типа можно использовать ключевое слово var:
Out аргументы широко используются в семействе методов Try. , где возвращаемое булево значение показывает успех операции и out аргументы содержат полученное значение:
Внимание: В данном примере i используется только внутри блока if, в котором определена, поэтому в Preview 4 этот пример тоже работает.
Одним из возможных улучшений (которое необязательно попадет в C# 7), может стать использование подстановочных символов ( * ) вместо тех out параметров, которые не будут использоваться далее. Например:
Сопоставление с шаблоном
В седьмой версии C# появляется понятие шаблона (pattern), который в общем случае представляет собой синтаксическую конструкцию, позволяющую проверить соответствие переменной определенному шаблону и извлечь из нее информацию, если такое соответствие имеется.
В C# 7 есть следующие шаблоны:
- Константные шаблоны: c (где c — константное выражение C#); проверяют, равна ли переменная этой константе или нет.
- Шаблоны типа: T x (где T — тип и x — переменная); проверяют, имеет ли переменная тип T, и если да, то извлекает ее значение в новую переменную x типа T.
- Var шаблоны: var x (где x — переменная); этот шаблон всегда вычисляется в true, используется для создания новой переменной того же типа и с тем же значением.
- is теперь может использоваться не только с типом, но и с шаблоном (в качестве правого аргумента).
- case в операторе switch теперь может использовать шаблоны, а не только константы.
Шаблоны с is
Рассмотрим простой пример, в котором используются и константный шаблон, и шаблон типа.
Как видно из примера, переменные шаблона (которые были объявлены в шаблоне), имеют ту же область видимости, что и out переменные, поэтому могут использоваться внутри внешнего блока видимости.
Внимание: В Preview 4 для переменных шаблона так же, как и для out переменных, действуют более строгие правила видимости, поэтому пример заработает только в следующих релизах.
Шаблоны и Try-методы могут использоваться вместе:
Шаблоны и выражение switch
Варианты использования switch были расширены, теперь можно:
- Использовать любые типы (не только примитивные).
- Использовать шаблоны в выражениях case.
- Добавлять дополнительные условия к выражениям case (используя ключевое слово when).
Отметим следующие особенности нового расширенного switch:
-
Порядок выражений case теперь имеет значение. Теперь логика сопоставления такая же, как у выражений catch: будет выбрано первое по порядку выражение, удовлетворяющее условию. Поэтому в данном примере важно, что более специфичное условие для квадрата идет перед более общим условием для прямоугольника, если поменять их местами, то условие для квадрата никогда не сработает. В таких случаях на помощь придет компилятор, который будет помечать явные недостижимые условия (так же, как и для catch). Данное изменение не является изменением уже существующего поведения: до C# 7 порядок выполнения выражений case не был определен.
Кортежи
Иногда хочется вернуть несколько значений из метода. Ни один из доступных на сегодняшний момент способов не выглядит оптимальным:
-
Out параметры: синтаксис выглядит перегруженным (даже если использовать рассмотренные выше нововведения), неприменимо к асинхронным методам.
Теперь метод возвращает 3 строки, объединенных в кортеж. Вызывающий код может их использовать следующим образом:
Имена полей Item1, Item2, . являются именами по умолчанию для каждого кортежа, однако, есть возможность дать данным, объединенным в кортеж, имена получше:
Теперь к элементам кортежа можно обратиться так:
Также имена элементов можно указать сразу в литерале кортежа:
Кортежи можно присваивать друг другу, если имена их элементов не совпадают: главное, чтобы сами элементы можно было присваивать друг другу. В будущем будут добавлены ограничения, в основном для литералов кортежей, которые будут сигнализировать о таких ошибках, как случайно поменянные местами имена элементов и т.д. В Preview 4 таких ограничений еще нет.
Кортежи представляют собой значимый тип, а их элементы — изменяемые открытые поля. Кортежи могут сравниваться на равенство: два кортежа равны (и имеют одинаковый хэш код), если все составляющие элементы равны друг с другом попарно (и имеют одинаковый хэш код). Такое поведение делает кортежи полезными не только для возвращения нескольких значений из метода. Например, если вам нужен словарь с составным ключом, используйте в качестве ключа кортеж. Если вам нужен список, где на каждой позиции должно быть несколько значений, также использует список кортежей. (От переводчика: не надо воспринимать это как руководство к использованию кортежей в 100% ситуациях, иногда простой класс с парой-тройкой свойств лучше выразит ваши намерения и будет легче поддерживаем в будущем).
Внимание: Кортежи в своей работе опираются на типы, которых еще нет в Preview 4, но их можно добавить в проект с помощью NuGet (не забудьте выбрать «Include prerelease» и указать «nuget.org» в качестве «Package source»), пакет называется System.ValueTuple.
Распаковка кортежей
Еще один способ работы с кортежем — его распаковка, которая заключается в присваивании его элементов новым переменным:
Также можно использовать ключевое слово var вместо типа для каждой переменной:
Или даже поместить var перед скобками:
Также распаковать кортеж можно в уже объявленные переменные:
Распаковать можно не только кортеж, любой тип может быть распакован. Для этого он должен иметь метод следующего вида:
Out параметры соответствуют значениям, которые будут присвоены в результате распаковки. Почему используются out параметры, а не кортежи? Чтобы можно было иметь несколько перегрузок метода с разным количеством параметров.
Такой подход позволит создавать «симметричные» конструктор и метод распаковки. Так же как и для out переменных мы планируем добавить подстановочные символы для игнорирования некоторых возвращаемых параметров.
Внимание: До сих пор неизвестно, будут ли добавлены подстановочные символы в C# 7.
Локальные функции
Иногда вспомогательная функция имеет смысл только внутри одного метода, в котором вызывается. Теперь такую функцию можно объявить внутри метода:
Аргументы внешнего метода и его локальные переменные доступны для локальной функции, так же как и для лямбда-выражений.
В качестве еще одного примера рассмотрим метод, реализованный как итератор. В этом случае такому методу обычно требуется неитерирующий метод-обертка для проверки аргументов (потому что сам итератор не вызывается, пока не будет вызван метод MoveNext).
С помощью локальных функций такая задача решается изящнее, чем обычно:
Если бы метод Iterator был обычным приватным методом, то он мог быть вызван случайно, без проверки аргументов. Кроме того, ему потребовалось бы передавать те же самые аргументы, что и методу Filter.
Внимание: В Preview 4 локальные функции должны быть объявлены перед вызовом. В будущем это ограничение будет ослаблено: локальные функции можно будет вызвать после того, как всем локальным переменные, ими используемые, будут присвоены значения.
Улучшения литералов
В C# 7 появилась возможность добавлять _ в качестве разделителя в числовые литералы:
Разделитель можно добавить в любом месте между цифрами, на значение он не влияет. Также в C# 7 появились бинарные литералы:
Локальные переменные и возвращаемые значения по ссылке
Теперь можно не только передать параметры в метод по ссылке (с помощью ключевого слова ref), но и возвратить данные из метода по ссылке, а также сохранить в локальной переменной тоже по ссылке.
Теперь будет удобно передавать ссылки на определенные места в больших структурах данных. Например, в игре информация содержится в большом заранее выделенном массиве структур (чтобы избежать пауз на сбор мусора). Теперь методы могут вернуть ссылку на одну из таких структур, с помощью которой вызывающий код может читать и изменять эту структуру.
Для того, чтобы работать с ссылками было безопасно, введены следующие ограничения:
- Можно возвращать только ссылки, которые возвращать безопасно: ссылки на объекты, переданные в метод и ссылки на поля объектов.
- Переменные инициализируются определенной ссылкой и в будущем не меняются.
Расширение списка типов, возвращаемых асинхронными методами
До сегодняшнего дня, async методы могли возвращать только void, Task or Task<T>. В C# 7 появилась возможность создания типов, которые также могут быть возвращены асинхронным методом. Например, можно создать структуру ValueTask<T>, которая поможет избежать создания объекта Task<T> в случае, когда результат асинхронной операции уже доступен. Для многих асинхронных сценариев, например, где используется буферизация, такой подход может значительно уменьшить число выделений памяти и таким образом повысить производительность.
Конечно, можно придумать и другие ситуации, в которых Task-подобные объекты будут полезны. Правильное создание таких типов не будет простой задачей, поэтому мы не ожидаем, что большое число разработчиков будут создавать их, однако мы думаем, что в различных фреймворках они будут полезны, и вызывающий код сможет просто использовать await, как сейчас для Task.
Внимание: В Preview 4 эти типы еще не доступны.
Больше членов класса в виде выражений
Методы и свойства в виде выражений (expression bodied members), появившиеся в C# 6, часто использовались, но не все типы членов класса можно было так объявлять. Теперь в C# 7 добавилась поддержка сеттеров, геттеров, конструкторов и деструкторов:
Это пример новой функциональности, добавленной сообществом, а не командой разработки компилятора! Ура, опен сорс!
Внимание: В Preview 4 поддержка этих членов класса недоступна.
Throw выражения
Выбросить исключение в середине выражения не так уж сложно: достаточно вызвать метод, который это сделает. Но в C# 7 теперь можно использовать throw как часть выражения: