heap.tech
лаборатория велосипедов
×

Новое в c# 7. Полгода спустя

21 ноября 2016
Пару месяцев назад я опубликовал статью о грядущих нововведениях в c# 7, какие-то вещи удалось рассмотреть достаточно детально, а по поводу других осталось больше вопросов, чем ответов. В этой статье я еще раз хочу сфокусироваться на новых фишках языка, благо на прошлой недели была трансляция connect() от MS.
Connect(); это такой meet-up для разработчиков, основной посыл - рассказать о новшествах в технологическом стеке Microsoft сообществу. Естественно меня заинтересовал блок о новых фичах в c# 7. Да, большинство из них я уже упоминал в прошлой статье, но есть и новые, о которых я ничего не слышал. Например деконструкторы. Еще более пристально удалось взглянуть на сопоставление с образом (pattern matching).

Deconstructors (деконструкторы)

Уверен, что все пользуются конструкторами, ну или, по крайней мере, знают что это такое. Конструктор позволяет программисту установить определенные значения или по умолчанию. Деконстурктор (внезапно!) позволяет сделать все с точностью, но наоборот. Деконструктор разбирает класс на составные и возвращает их. Деконструктор, как и конструктор, не может возвращать значений, поэтому все параметры возвращаются по ссылке (out).

Пример
public class Person { public string FirstName { get; } public string LastName { get; } public string SurName { get; } public int Id { get; } public Person(string firstName, string lastName, string surName, int id) { FirstName = firstName; LastName = lastName; SurName = surName; Id = id; } public void Deconstruct(out string firstName, out string lastName, out string surName, out int id) { firstName = FirstName; lastName = LastName; surName = SurName; id = this.Id; } }

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

Пример вызова деконструктора
Person person = new Person("Ivan", "Ivanov", "Ivanovich", 1); (string firstName, string lastName, string surName, int id) = person;

Или так
string firstName, lastName, surName = null; int id = 0; (firstName, lastName, surName, id) = pathInfo;

А еще можно вот так
var(firstName, lastName, surName, id) = pathInfo;

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

Pattern Matching (сопоставление с образом)

Pattern Matching дает возможность разработчику сравнивать тип объекта с некоторым шаблоном и, если типы двух объектов эквивалентны - выполнить приведение. Это упрощает жизнь и ускоряет процесс разработки. Раньше приведение порождало много неоднородного кода, например оператор AS, явное приведение (explicit casting) с отловом ошибок и оператор IS, возвращающий true, если объект не null и может быть приведен к предоставленному типу. Но это было раньше, а сейчас у нас появился pattern matching.

Например, есть базовый объект машина и производные классы: электрокар, машина с бензиновым и водородным двигателями. Допустим, водитель, севший в машину, хочет ее завести, но он понятия не имеет в машине с каким двигателем он оказался — поэтому выведем ему сообщение на лобовое стекло (комплектация дорохобохато, поэтому проекция, жесты и прочая годнота)

Реализация классов
abstract class Car { public abstract bool IsReadyToStart(); public abstract void StartEngine(); public void WindshieldPrintString(string text) { Console.WriteLine(text); //draw text on the car windshield through the holographic projection } } class Gasoline : Car { public override bool IsReadyToStart() { var fuelTankEmpty = false; if (!fuelTankEmpty) { return true; } return false; } public override void StartEngine() { //gasoline car start engine } } class Hyndrogen : Car { public override bool IsReadyToStart() { bool hydrogenTankEmpty = false; bool isFuelSystemDamaged = false; if (!hydrogenTankEmpty && !isFuelSystemDamaged) { return true; } return false; } public override void StartEngine() { //hydrogen car start engine } } class Electocar : Car { public override bool IsReadyToStart() { bool isBatteryLow = false; bool isBatteryDamaged = false; if (!isBatteryLow && !isBatteryDamaged) { return true; } return false; } public override void StartEngine() { //electric car's engine could not be started previously //just push down the accelerator and move on! } }

Как раньше
static void Main(Car driversCar) { if (driversCar == null) { throw new ArgumentNullException(); } if (driversCar is Gasoline) { var gasolineCar = (Gasoline)driversCar; if (gasolineCar.IsReadyToStart()) { gasolineCar.StartEngine(); gasolineCar.WindshieldPrintString("Gasoline engine started"); } } else if (driversCar is Hyndrogen) { var hydrogenCar = driversCar as Hyndrogen; if (hydrogenCar.IsReadyToStart()) { hydrogenCar.StartEngine(); hydrogenCar.WindshieldPrintString("Hydrogen engine started"); } } else if (driversCar is Electocar) { var electrocar = driversCar as Electocar; if (electrocar.IsReadyToStart()) { electrocar.StartEngine(); electrocar.WindshieldPrintString("Just push down the accelerator and move on!"); } } else throw new NotImplementedException(); }

А сейчас можно так
static void Main(Car driversCar) { if (driversCar is null) { throw new ArgumentNullException(); } else if (driversCar is Gasoline gasolineCar && gasolineCar.IsReadyToStart()) { gasolineCar.StartEngine(); gasolineCar.WindshieldPrintString("Gasoline engine started"); } else if (driversCar is Hyndrogen hydrogenCar && hydrogenCar.IsReadyToStart()) { hydrogenCar.StartEngine(); hydrogenCar.WindshieldPrintString("Hydrogen engine started"); } else if (driversCar is Electocar electrocar && electrocar.IsReadyToStart()) { electrocar.StartEngine(); electrocar.WindshieldPrintString("Just push down the accelerator and move on!"); } else throw new NotImplementedException(); }

Круто же, 27 строк против 38. Больше выразительного кода. А еще можно через switch
switch(driversCar) { case Gasoline gasolineCar when gasolineCar.IsReadyToStart(): gasolineCar.StartEngine(); gasolineCar.WindshieldPrintString("Gasoline engine started"); break; case Hyndrogen hydrogenCar when hydrogenCar.IsReadyToStart(): hydrogenCar.StartEngine(); hydrogenCar.WindshieldPrintString("Hydrogen engine started"); break; case Electocar electrocar: if (electrocar.IsReadyToStart()) { electrocar.StartEngine(); electrocar.WindshieldPrintString("Just push down the accelerator and move on!"); } break; case null: default: throw new ArgumentNullException(); }

Сразу бросается в глаза оператор when. Он добавляет еще одно условие, вложенное в условие конкретного блока (case). Остается только гадать, поддерживается ли множественное when, например, через запятую.
Кроме этого, условия pattern matching проверяются на этапе компиляции. Но это не значит, что данный код не скомпилируется — все исключения в рантайме буду происходить в точности, как они описаны.
 
1832
0

Оставлять комментарии могут только зарегистрированные пользователи

пока никто не оставлял комментариев