О поддержке языковых фич C# в Visual Studio и в CodeRush for Roslyn

    C# постоянно развивается. Весной вышла уже седьмая версия. В этой статье будет обзор поддержки последних фич C# в CodeRush for Roslyn. Про C# 7.0 уже было несколько публикаций на хабре, поэтому основное внимание именно на то, как это поддерживается в CodeRush for Roslyn.


    Бонусом, в конце статьи, дадим рецепт для тех, кто по каким то причинам не хочет использовать новые языковые фичи.



    Task-like возвращаемые типы асинхронных методов


    Если раньше асинхронный метод мог возвращать только типы Task или Task<T>, то теперь возможно объявить собственные Task-like типы, которые можно использовать в возвращаемых значениях асинхронных методов.


    Во-первых, давайте проверим как работают фичи внутри асинхронного метода, который возвращает Task-like тип. Например, для метода с одним параметром попробуем вызвать Exit Method Contract:



    Как видим, CodeRush for Roslyn корректно определил, что return-оператор должен быть пустым и не должен содержать никакого выражения, т.к. в данном случае возвращаемый тип не является универсальным (не содержит параметров типа). Другие фичи, которые генерируют return-операторы также работают корректно. В качестве примера, давайте посмотрим как отработает шаблон "r", который вызывает Smart Return, внутри асинхронного метода:



    В этом случае CodeRush правильно распознал, что в return-операторе должно содержаться выражение типа ArgumentKind и вставил соответствующее значение по-умолчанию.


    Второй момент — это фичи, которые используют await-выражения с вызовом асинхронного метода, возвращающего Task-like тип. Как видно на следующем скриншоте, CodeRush правильно определяет тип таких await-выражений:



    Кортежи значений


    Пожалуй, это нововведение в спецификациях языков претендует на роль самого востребованного. Теперь с помощью удобного синтаксиса можно объявлять типы, являющиеся кортежами нескольких значений. Можно задавать как просто типы элементов, так и их имена. Для будущих релизов у нас есть несколько идей по наиболее обширной поддержке кортежей: определять и удалять неиспользуемые пункты, менять пункты местами, использовать кортежи в рефакторинге Convert to Tuple и др. Пока же имеется поддержка кортежей в уже имеющихся фичах. Продемонстрируем это на примере рефакторинга Add Parameter:



    Рефакторинг корректно распарсил введённое значение как кортеж из SortingKind и SortingOrder и в качестве дефолтного значения подставил кортеж из дефолтных значений этих типов.


    В качестве ещё одного примера продемонстрируем работу фичи Smart Return, которая вызывается раскрытием шаблона r:



    Как видим, CodeRush for Roslyn использует имена для переменных кортежа, если они были объявлены.


    Вложенные локальные функции


    Порой возникает необходимость написания вспомогательной функции использование, которой ограниченно определённым методом. В C# 7 возможно объявить локальную функцию прямо в теле метода. Локальные функции схожи с лямбда-выражениями, но зачастую код использование локальных функций является более наглядным и понятным.


    Во-первых, мы обновили фичу Naming Assistant чтобы она работала с локальными функциями:



    А еще, уже знакомый Add Parameter тоже теперь работает с новым синтаксисом:



    Рефакторинг правильно нашёл декларацию и все ссылки и добавил параметр в нужные позиции.


    Расширение использования expression-body и throw-expression


    Думаю, что многим пришлось по душе использование expression-body в C# 6, теперь список элементов, где он может быть расширен акцессорами свойств, конструкторами и деструкторами. Рефакторинг Use Expression Body доступен на новых элементах:



    Также и обратные данному рефакторингу Expand Method и Expand Accessor доступны, если использован новый синтакс.



    Use Expression Body доступен теперь и в тех случаях, когда в теле метода/акcессора отсутствует имплементация и присутствует лишь вызов исключения. С появлением throw-expression запись таких методов можно сделать короче и нагляднее:



    Ещё один случай, где throw-expression повышает наглядность кода — это тернарные операторы. Теперь if/else-выражение, в котором в зависимости от условия либо вызывается исключении, либо возвращается/присваивается какое-то значение, можно заменить одним выражение с тернарным оператором. Поможет в этом рефакторинг Compress to Ternary Expression:



    Обратный рефакторинг Expand Ternary Expression конечно же также будет доступен на выражениях с throw-expression:



    Сопоставление с образцом


    Тоже очень мощное нововведение, которое, уверен, уже полюбилось многим пользователям. Оно позволяет в операторах if и case проверить, что переменная является объектом определённого типа и тут же объявить переменную этого типа, избегая лишнего кастинга. В case-операторах в дополнение к этому можно осуществить дополнительные проверки в when-выражении.


    В данном разделе мы тоже пока не воплотили все свои идеи. В качестве примера того, что уже имеется, продумонстрируем работу фичи Declare Class на case-выражении, в котором использован новый синтакс:



    В данном случае CodeRush for Roslyn корректно определил, что здесь используется шаблон типа, а также сразу задекларировал свойство, используемое в when-выражении. Давайте теперь отладим данный метод при этом включим CodeRush Debug Visualizer:



    Когда отладчик доходит до switch-оператора, Debug Visualizer вычисляет выражения во всех case-ветках и отображает какая из них будет выполняться в данном случае, делая код остальных веток более блеклым. Это делает отладку кода более наглядной и удобной, показывая какой код будет выполняться на следующем шаге.


    Возврат по ссылке


    Теперь ссылка на переменную может использоваться не только в параметре, но и в возращаемом типе метода. Также можно объявлять локальные переменные, которые будут содержать ссылку на переменную.


    Возьмём связку из двух уже знакомых нам фич: Smart Return и Declare Method и посмотрим как они отработают в методе, который возвращает значение по ссылке:



    Smart Return подставил ключевое слово ref, поскольку return-оператор должен содержать ref-выражение в данном случае. Declare Method, определив, что метод вызывается в ref-выражении, объявил корректный тип ref SyntaxNode.


    Бинарные литералы


    Теперь значения каких-то констант в коде можно задавать, используя двоичный код. Это может быть удобным в перечислениях. CodeRush имеет в своём арсенале фичу, которая позволяет ускорить и упростить задачу добавления нового элемента — Smart Duplicate Line. Достаточно нажать Shift + Enter и CodeRush добавит новый элемент, выделив текстовыми полями те элементы, которые потребуют изменений:



    И обещанный бонус


    Мало кто знает, но на уровне проекта можно выбирать версию языка. CodeRush for Roslyn учитывает опцию Build | Advanced | Language Version.



    Например, если поставить C# 4.0, то контекстное меню изменится, потому что интерполяцию строк придумали в C# 6.0.



    Но мы не просто скрываем неподдерживаемые пункты меню по условию, мы реализуем полноценную поддержку нужной версии языка на в том числе на уровне генерации кода.


    Например, если в проекте стоит версия C# 6.0, то Declare Comparison members из Declare Menu сгенерит код таким образом:


    // ...
    Class1 other = obj as Class1;
    if (other == null) {
       throw new ArgumentException(nameof(obj) + " is not a " + nameof(Class1));
    }
    // ...

    А в C# 3.0, например, nameof() еще не было, и код будет таким:


    // ...
    Class1 other = obj as Class1;
    if (other == null) {
        throw new ArgumentException("obj is not a Class1");
    }
    // ...

    За счет использования штатных парсеров студии CodeRush for Roslyn одинаково хорошо поддерживает как новые возможности C#, так и старые. CodeRush значительно расширяет возможности Visual Studio не замедляя ее. Особенно это заметно на серьезных enterprise-проектах с большим объемом кода.


    Скачать пробную версию CodeRush for Roslyn можно на нашем сайте.

    • +17
    • 6,6k
    • 6
    Developer Soft
    67,76
    Компания
    Поделиться публикацией

    Комментарии 6

      0
      Вопрос в сторону, а зачем могут понадобится свои Task like типы? Немогу представить большую абстракцию, чем Task, видимо нужна меньшая только зачем? Зачем различать Task и? Или это только чтобы генерик параметр «скрыть»?
        0
        Тут хорошо с примерами написано почему это добавили в язык. У меня тоже пока не возникало необходимости в написании кастомных Task'ов.
          0

          Вот пример использования. Асинхронные итераторы.
          https://github.com/Andrew-Hanlon/AsyncEnumerator

            0
            Спасибо. А можете помочь мне представить как это работает, как хэндлер/обработчик кому нужна синхронность (например для сериализации) себя поведет получив таск-
            «асинхронный итереатор». И если неправильно, то разве это не получается leak abstraction?
          0
          Smart Return подставил ключевое слово ref, поскольку return-оператор должен содержать ref-выражение в данном случае. Declare Method, определив, что метод вызывается в ref-выражении, корректно объявил возвращаемый тип как ref int.


          Не могли бы пояснить? Если я правильно понял, цитата и демо работы расходятся.
            0

            Спасибо за замечание, кажется мы меняли пример и не обратили внимание на то что это уже не int. Поправил.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое