Возможности C# 8.0: взгляд в будущее

Tags: C#, Microsoft

Прошло почти 20 лет с тех пор, как Microsoft выпустила первую версию языка C#. Если сначала по мнению многих этот язык был просто копией Java, то с тех пор C# значительно эволюционировал.

Сегодня он является одним из самых широко используемых и любимых языков программирования. Вы можете использовать его для разработки настольных, веб-и мобильных приложений, а написанный вами код  будет работать во всех основных операционных системах. Или вы можете погрузиться в IOT (Интернет вещей) и написать код, который сделает ваш дом “умным”. Мы и в самом деле живем в интересное для разработчиков  C# время.

Если настоящее уже столь захватывающее, то чего же ждать от будущего? Можем ли мы узнать, каковы перспективы для языка?

Конечно, можем.  Microsoft уже давно разработала C# «в открытом доступе». Вы можете просто почитать репозиторий GitHub и участвовать в обсуждениях и предложениях (почему бы и нет?)

Сегодня мы выбрали три предложения функций для C# 8.0 для обсуждения: общее расширение, реализация по умолчанию на интерфейсах и типы ссылок с нулевым значением.

Общее расширение

Общее расширение, пожалуй, является наименее спорным из этих трех предложений функций, и, в некотором роде, оно также является наименее склеенным. Поэтому я решил, что это станет хорошей отправной точкой.

Вероятно, вы знакомы с методами расширения, представленными на C# 3.0 в 2007 году. И хотя это совершенно возможно для их злоупотребления, нельзя отрицать, что методы расширения, если их использовать разумно, могут быть прекрасным дополнением к инструментарию разработчика C #, но не необходимостью для LINQ.

В какой-то момент вы, возможно, задавались вопросом: почему только методы расширения? И вы были в этом не одни. Например, свойства расширений были желанием разработчиков C# довольно долгое время, о чем свидетельствует этот вопрос на Stack Overflow 2009 года или даже на более ранней ветке форума в 2007 (!).

Но теперь кажется, что ожидание, наконец, подходит к концу. По словам Мэдса Торгерсена, менеджера программ для команды разработчиков C#, стажер Microsoft предложил новый синтаксис для методов расширения, который также позволит «расширять другие компоненты».

Давайте посмотрим код.

Вы наверное очень хотите посмотреть какой-нибудь код? Ниже приведен пример метода расширения с использованием текущего синтаксиса:

public static class IntExtensions

{

  public static bool Even(this int value)

  {

       return value % 2 == 0;

  }

}

Пока здесь нет ничего удивительного, не так ли? В исходном синтаксисе метод расширения является только статическим методом в статическом классе с ключевым словом перед первым параметром.

Дело в том, что этот синтаксис работает только с методами (и, действительно, как это могло бы быть иначе, тогда что было бы первым «параметром» свойства?)

Здесь идет «общее расширение». Это словосочетание означает новый тип описания, называемого «расширением»:

public extension IntExtension extends int

{

   public bool Even => this % 2 == 0;

}

Приведенный выше код является примером; на дату написания этой статьи команда C# еще не определилась с синтаксисом.

Независимо от того, пример показывает создание класса расширения, который расширяет «int». Затем мы описываем свойство, как мы обычно делаем, и все готово.

Теперь вызывающий код сможет использовать свойство как обычно:

int x = int.Parse(Console.Readline());

if (x.Even)

{

   Console.WriteLine(“You’ve typed an even number.”);

}

Эта новая функция предназначена для поддержки методов, свойств, операторов и статических элементов, по крайней мере для начала; события и конструкторы могут прийти позже.

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

Реализация по умолчанию на интерфейсах

Было немного странно услышать о реализации по умолчанию на интерфейсах из лекции Мэдс Торгерсен , прошедшей в мае 2017 года на конференции Build 2017. У многих наверняка сразу возникли вопросы: «Как насчет типов записей?», «Как насчет непреложных объектов? Реализация на интерфейсах это действительно то, что мы получаем?»

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

interface IDisplayService

{

   void DisplayMessage(string message) { WriteLine(message); }

}

В C # 8.0 приведенный выше код был бы совершенно законным. Класс, реализующий интерфейс, не будет нуждаться в реализации метода. Если некоторая реализация интерфейса решает, что реализация по умолчанию не соответствует его потребностям, то она может обеспечить свою собственную реализацию.

Помимо методов, вам также будет разрешено добавлять тела для индексаторов, свойств и аксессуаров событий. Также будут допущены статические элементы, такие как методы, свойства и индексы.

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

Случаи использования

Как указано в предложении функции, основной пример использования методов интерфейса по умолчанию - это позволить разработчику безопасно развивать интерфейс. Вы могли бы добавить к нему новые методы, и, если бы вы предоставили стандартную реализацию, существующие клиенты интерфейса не были бы вынуждены ее реализовать.

В настоящее время вы можете решить эту проблему с помощью метода расширения.

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

Именно тогда то и нужна реализация по умолчанию.

Еще одно важное преимущество реализации по умолчанию для интерфейсов относится к Android и iOS. Поскольку Java и Swift предлагают эту функцию, сложно использовать C# для переноса API Android / iOS, которые ее используют. C# 8.0 позволит более точно обернуть эти API.

Станут ли интерфейсы абстрактными классами?

В каком-то роде да, но не совсем.  Как вы знаете, на таких языках, как C# и Java, нет множественного наследования, что означает, что вы не можете наследовать более одного класса. С другой стороны, класс способен (и будет способен в дальнейшем) реализовать несколько интерфейсов.

Обнуляемые ссылочные типы

«Что?», - возможно спросите вы. «Разве это не должны быть «необнуляемые ссылочные типы»?» Название этой функции может быть немного запутанным, давайте сначала разберемся, в чем преимущество этой функции.

Вы наверняка знакомы с  фразой «ошибка в миллиард долларов». Вы вряд ли делали подобное по неосторожности, это выражение относится к нулевой ссылке, и она была придумана не кем иным, как сэром Тони Хоаре, который и изобрел нулевую ссылку.

Но что в этом плохого?

Аргументов несколько, но самой большой проблемой является риск получить пресловутое исключение для NULL-ссылки (например, исключение нулевого указателя в Java-land). Поскольку все («все» в контексте C#, означающее все ссылочные типы) может быть нулевым, вы всегда рискуете получить исключение, когда пытаетесь получить доступ к некоторому члену объекта.

Одна из самых больших проблем здесь заключается в том, что на самом языке отсутствует синтаксис, который позволил бы автору выразить свое намерение. Вы не можете сказать «Этот параметр никогда не может быть пустым» или «Это свойство может иногда иметь значение null».

Это распространено для  функциональных языков иметь дело с определенным типом, представляющим концепцию потенциального отсутствующего значения, часто называемого «Maybe» или «Option».

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

Давайте вернемся к проблеме именования. Имеет смысл говорить об обнуляемых типах значений, поскольку типы значений по умолчанию не являются обнуляемыми. Но в случае ссылочных типов это не так, верно? В конце концов, они были обнуляемы с самого начала.

Здесь то и есть ловушка:  команда разработчиков C# в движении, не лишенном споров, намеревается сделать новое значение необнуляемым по умолчанию для ссылочных типов. В этом смысле «новыми» будут типы с нулевым значением.

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

Хватит говорить. Давайте посмотрим код!

Я полагаю, что использование печально известного класса “Person” в качестве примера является программным эквивалентом игры «Лестница в небо» в магазине музыкальных инструментов - возможно, слегка преувеличено. Но использование более сложного примера было бы как отвлекающим, так и ненужным.

Предположим, что у нас есть класс «Person» со свойством «Age» типа «int». Затем мы напишем следующее:

public int CalculateSquareOfAge(Person p)

{

   int age = p.Age;

   return age * age;

}

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

C# 8 обещает изменить это, сделав по умолчанию ссылочные типы, не имеющие значения NULL. В приведенном выше примере попытка получить доступ к свойству «Age» будет безопасным, поскольку «p» никогда не будет равным нулю.

Если вы хотите, чтобы значение «p» было равно NULL, вам нужно добавить знак вопроса к типу, как было уже сказано ранее:

public int CalculateSquareOfAge(Person? p)

{

   int age = p.Age;

   return age * age;

}

Теперь, когда «p» может быть нулевым, попытка получить доступ к Age уже не безопасна: вы получите предупреждение для этого. Хотите избавиться от предупреждения?

Просто делайте то, что вы, скорее всего, уже сделали сегодня, и выполните нулевую проверку:

public int CalculateSquareOfAge(Person? p)

{

   var result = 0;

   if (p != null)

       result = p.Age * p.Age;

 

   return result;

}

Таким образом, будет выполнен анализ потока; если линия назначения достигнута, компилятор будет знать, что «p» не может быть нулевым  и оставит вас в покое.

Вот еще одна возможность:

public int CalculateSquareOfAge(Person? p)

{

   int age = p?.Age ?? 0;

   return age * age;

}

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

Предотвращение присвоения значения от обнуляемого к необнуляемому

Новая версия C# также предотвратит присвоение переменной с нулевыми значениями необнуляемое значение, поэтому приведенный ниже код также приведет к предупреждению:

string? nullableString = “hello”;

string nonNullableString = nullableString;

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

Одним из таких примеров может быть использование метода string.IsNullOrEmpty ():

public void Foo(string? bar)

{

    if (!bar.IsNullOrEmpty())

    {

         var length = bar.Length;

    }

}

Вышеприведенный код генерирует предупреждение, несмотря на то, что «bar» не может быть нулевым. Вот когда этот оператор пригодится:

public void Foo(string? bar)

{

    if (!bar.IsNullOrEmpty())

    {

         var length = bar!.Length;

    }

}

Имейте в виду, что, используя этот оператор, вы в основном говорите компилятору: «Поверьте мне! Я знаю, что делаю ». Значит, вам лучше знать, что вы делаете!

Как насчет обратной совместимости?

Я знаю, о чем вы, наверное, думаете. «Это звучит хорошо и все, но разве это изменение не нарушит много существующего кода?»

Ну, конечно. Вот почему это будет изменение выбора, активированное на основе каждого проекта.

C# имеет будущее. И оно выглядит потрясающе

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

Как вы можете видеть, развитие языка - это тяжелая работа. Язык должен оставаться совместимым с миллионами (или даже миллиардами) строк кода, написанных с его помощью за последние 17 лет. В то же время, чтобы продолжать оставаться актуальным, он должен удовлетворять потребности разработчиков, которые сталкиваются с проблемами, которые были невообразимыми много лет назад. И он должен делать все это, не теряя своей сути: быть простым и доступным объектно-ориентированным, как язык C#.

No Comments

Add a Comment