Обзор новых элементов C# 6.0

В сети находится огромное количество информации по этой теме и все достаточно разрозненно. Хотелось прежде всего для себя систематизировать и обозначить новые возможности языка. Некоторые вещи являются синтаксическим сахаром, некоторые являются действительно новыми фичами. Хотя в свете возможностей предоставляемых Roslyn, понятие о «сахаре» и новых фичах несколько размывается. Ведь можно модифицировать язык и создавать свои диалекты. Так что возможно скоро большие команды будут использовать свои варианты языка. Или нет. Но это отдельная большая тема (вспоминается просто Jetbrains с их Resharper). Доступность описываемых фич описана на странице https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status. Большая часть всех фич уже доступна и вызывает положительные эмоции. Хотя, честно сказать, не всё из того, что помечено как «должно работать» работает. Например, индексированные члены в словаре что-то не завелись, хотя остальное все работало как надо.

Активизация экспериментального компилятора

Для того, чтобы описываемые штуки завелись, необходимо использовать VS2014 cRoslynSDK, или же VS2013 с SDK, RoslynSDK. После того как все компоненты были установлены, можно создать новый проект, открыть файл проекта (.CSPROJ) в текстовом редакторе и добавить в конфигурацию сборки строку:

<LangVersion>experimental</LangVersion>

Обратите внимание, что именно с маленькой буквы надо писать «experimental», иначе не работает. Конечно, это будет поправлено ближе к релизу. После таких нехитрых приготовлений можно начинать эксперименты.

Краткий список нововведений

  • Exists: Already shipped in previous release
  • Done: Implemented for this release
  • Planned: Intended for this release
  • Maybe: Possibly intended for this release
  • Withdrawn: Probably not in this release
  • No: Not intended for this release

 

Feature Example C#
Primary constructors class Point(int x, int y) { … } Done
Auto-property initializers public int X { get; set; } = x; Done
Getter-only auto-properties public int Y { get; } = y; Done
Using static members using System.Console; … Write(4); Done
Dictionary initializer new JObject { [«x»] = 3, [«y»] = 7 } Done
Indexed member initializer new JObject { $x = 3, $y = 7 } Withdrawn
Indexed member access
  1. $name = c.$first + » » + c.$last;
Withdrawn
Declaration expressions int.TryParse(s, out var x); Done
Await in catch/finally try … catch { await … } finally { await … } Done
Exception filters catch(E e) if (e.Count > 5) { … } Done
Typecase Select Case o : Case s As String : … No
Guarded cases Select Case i : Case Is > 0 When i Mod 2 = 0 No
Partial interfaces Partial Interface I1 Exists
Binary literals 0b00000100 Planned
Digit separators 0xEF_FF_00_A0 Planned
Expression-bodied members public double Dist => Sqrt(X * X + Y * Y); Planned
Event initializers new Customer { Notify += MyHandler }; Planned
Null propagation customer?.Orders?[5]?.$price Done
Semicolon operator (var x = Foo(); Write(x); x * x) Maybe
Private protected private protected string GetId() { … } Withdrawn
Params IEnumerable int Avg(params IEnumerable<int> numbers) { … } Planned
Constructor Inference new Tuple(3, «three», true); Maybe
String interpolation «\{p.First} \{p.Last} is \{p.Age} years old.» Maybe
TryCast for nullable var x = TryCast(u, Integer?) Exists
Implicit implementation Class C : Implicitly Implements I Exists
NameOf operator string s = nameof(Console.Write); Planned

Индексированные члены и инициализация элементов

Для начала можно рассмотреть следующий код:

 

По большому счету пример не что иное как коллекция типа «имя-ключ». В новой версии языка можно использовать индексную запись, т.е. моментальное присвоение во время инициализации.

Такой фокус можно проворачивать и с другими типами, не только строками. Что в целом очевидно должно быть =).

Новый оператор $

Вместе с общим индексированием вводится специальный оператор $, которые помогает в индексировании строковых значений. Пример выше с помощью оператора $ можно переписать следующим образом:

Фактически новый оператор позволяет работать со строковыми ключами в упрощенно манере. Причиной возникновения такого оператора является на мой взгляд широкое использование jsonсериализации, работа с которой значительно упрощается, используя такой подход. Т.е. следующие строки кода эквиваленты:

Использование оператора $ не вызывает к жизни статическую проверку компилятором кода. Т.е. любое имя после $ будет легально. Что впрочем справедливо и для старого подхода с индексатором. Если вы ошиблись с именем, то будет исключение доступа по ключу. Полное раскрытие оператора $ происходит в jsonи других форматах со слабой типизацией.

Для JObject используется nuget пакет newtonsoft.json.

Автосвойства с инициализаторами

Инициализация класса порой может быть весьма затратна по времени. Для примера можно взять следующий кусочек кода:

В новой версии языка возможно сократить этот код с помощью автосвойства с инициализатором:

Свойство осталось в состоянии «только для чтения». Однако инициализацию можно применять не только для свойств чтения. Пример ниже покажет как можно использовать «первичный конструктор» и инициализатор для автосвойств с getи set.

Первичный конструктор

Для сравнения сначала будет пример класса в текущем варианте фреймворка, и далее с учетом новых возможностей С# 6.0.

В этом примере можно отметить несколько вещей:

  • Необходимость проверки свойства Titleвынуждает ввести приватное поле класса.
  • Конструкторы имеют повторяющуюся структуру, некоторая вариация копи-пасты.
  • Имя «Title» появляется в коде 7 раз в различных вариантах написания в весьма простом сценарии.
  • Инициализация свойств требует явного указания их в конструкторе.

В целях упрощения синтаксиса, но оставляя все приятные бонусы C# можно использовать инициализаторы свойств и первичные конструкторы.

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

Кроме всего прочего, возможно использование модификаторов в первичных конструкторах.

Стало:

Экономия кода, сил и времени на лицо. В процессе рассмотрения фичи с коллегами, некоторые высказывались, что это совсем все непонятно. Но это можно списать на ретроградство. =)

Использование статических классов

Данная фича языка относится полностью к «синтаксическому сахару». Теперь статические классы можно указать в usingсекции и в коде использовать уже сами методы. Двоякое отношение к этой фиче, так как теперь будет несколько менее понятно, чей метод используется. Не так явно будет бросаться в глаза нарушение принципов хорошего написания кода. Широкое использование статических классов явно намекает на проблемы в архитектуре. После того, как высказал опасения, можно перейти к сути.

Все внимание на то, как производится вывод аргументов на консоль. Если не обращать внимание на секцию usingили если она скрыта, то возникает полное ощущение того, что метод принадлежит классу. Я думаю, что данную фичу стоит использовать только с хорошо знакомыми статическими классами и не использовать ее для самописных классов. Работая со стандартными статическими классами фича позволяет сократить объем кода и убрать утомительные наборы одно и того же текста. Так же пока что идет обсуждение, как работать с методами расширений. Так как они реализованы так же через статические классы. И указание этих классов в usingдолжно приводить к какому результату?

Объявление выражений

Не редкой является ситуация, когда в процессе написания какого-то выражения, необходимо ввести новую переменную, для использования именно в этом выражении. Легко представить себе следующие ситуации:

  • Работа с TryParse() методом подразумевает, что вам необходима переменная в которую запишется или не запишется результат парсинга. У меня кажется еще не было случая, когда я сначала обозначил переменную. А потом написал TryParse(). Всегда приходится условно «возвращаться назад».
  • При работе с LINQпорой необходимо закэшировать результат вычислений, для того, чтобы не вызывать расчет запроса множество раз. Для того, чтобы это достичь, вы прерываете процесс и вводите новую переменную.

В общем можно использовать объявление переменных прямо в методе.

Интересующий нас метод находится на третьей строке. Вместо того, чтобы заранее определить attributeValue– переменная определяется прямо в вызове. Наиболее интересным вопросом наверно является какова область видимости таких переменных? Области видимости определяется родительским выражением. Т.е. в данном случае область видимости ограничивается методом, где объявляется переменная. Именно поэтому переменная attributeValue доступна в блоке else. По поводу использования varв объявлении переменных: общей рекомендацией является использование var, кроме тех случаев, когда тип переменной не выводится однозначно.

Условия на исключительные ситуации

Теперь блок catchпроверяет не только тип исключения, но и выполнение условных операторов внутри блока. В примере ниже, исключение будет обработано только если исключений произошло больше 5. В противном случае, исключение пойдет выше по стеку.

Дополнительные форматы чисел

В новой версии языка можно будет использовать форматирование чисел поразрядно для большего удобства. Это может быть применено к десятичной, шестнадцатеричной или двоичной форме чисел. Это чаще всего может быть полезно при работе с флагами. Пример работы с простыми числами:

Работа с флагами:

Оставить комментарий