Советы

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

77

Программы — Visual Studio — Рефакторинг кода

Существует много способов вызвать инструменты для рефакторинга в системе Visual Studio 2013, включая контекстное меню, которое открывается после щелчка правой кнопкой мыши, интеллектуальные дескрипторы и меню Refactor в главном меню (только для разработчиков, работающих на языке программирования C#).

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

Полный список действий по рефакторингу, доступных для разработчиков C# в системе Visual Studio 2013, включает операции Rename, Extract Method, Encapsulate Field, Extract Interface, Remove Parameters и Reorder Parameters.

Можно также использовать операции Generate Method Stub и Organize Usings, которые с некоторой натяжкой также можно отнести к рефакторингу.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

Встроенная поддержка рефакторинга, предлагаемая системой Visual Studio 2013 для разработчиков проектов на языке VB, ограничена символическими методами Rename и Generate Method Stub.

Операции рефакторинга

В следующих разделах описывается каждая из операций рефакторинга и приводятся примеры использования встроенной поддержки рефакторинга в языках C# и VB.

Операция Extract Method

Чтобы начать рефакторинг длинного метода, лучше всего разбить его на несколько небольших частей. Для этого необходимо выделить область кода, которую разработчик хочет удалить из исходного метода, и выбрать команду Extract Method из контекстного меню.

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

После подтверждения имени нового метода его код вставляется сразу после исходного метода. Удаленный блок метода заменяется вызовом нового метода.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

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

private void button1_Click(object sender, EventArgs e)
{
string connectionString = Properties.Settings.Default.ConnectionString;

// Выделите текст с этого места
if (connectionString == null)
{
connectionString = «DefaultConnectionString»;
}
// до сюда, и вызовите команду Extract Method

MessageBox.Show(connectionString);
/* … Продолжение длинного метода … */
}

На этом месте автоматически генерируется следующий код:

private void button1_Click(object sender, EventArgs e)
{
string connectionString = Properties.Settings.Default.ConnectionString;

connectionString = ValidateConnectionString(output);

MessageBox.Show(connectionString);
/* … Продолжение длинного метода … */
}

private static string ValidateConnectionString(string connectionString)
{
if (connectionString == null)
{
connectionString = «DefaultConnectionString»;
}

return connectionString;
}

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

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

На следующем шаге после указания нового свойства следует определить, какие ссылки на переменную класса следует заменить ссылкой на новое свойство. Откроется окно предварительного просмотра по завершении поиска ссылок. В верхней панели окна размещено дерево, в котором указываются поля и методы, имеющие ссылки на данную переменную.

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

Переменная класса устанавливается закрытой, а соответствующие ссылки обновляются.

Операция Extract Interface

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

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

В следующем примере требуется извлечь в отдельный интерфейс первый метод:

public class ConcreteClass
{
public void ShouldBeInInterface()
{ /* … */ }

public void AnotherNormalMethod(int ParameterA, int ParameterB)
{ /* … */ }

public void NormalMethod()
{ /* … */ }
}

После выбора команды Extract Interface из контекстного меню, появляющегося после щелчка правой кнопкой мыши, и выбора метода ShouldBeInInterface, подлежащего извлечению, в диалоговом окне Extract Interface (как показано на рисунке выше), в новом файле возникает новый интерфейс, а исходный файл обновляется следующим образом:

interface IBestPractice
{
void ShouldBeInInterface();
}

public class ConcreteClass: Chapter08Sample.IBestPractice
{
public void ShouldBeInInterface()
{ /* … */ }

public void NormalMethod(int ParameterA, int ParameterB)
{ /* … */ }

public void AnotherNormalMethod()
{ /* … */ }
}

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

Диалоговое окно Reorder Parameters dialog, показанное на рисунке ниже, позволяет переставлять параметры в списке в соответствии с требуемым порядком:

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

Как только требуемый порядок будет достигнут, пользователь может предварительно просмотреть результат. По умолчанию параметры в каждом вызове данного метода переставляются автоматически в соответствии с новым порядком. Диалоговое окно Preview, аналогичное показанному на рисунке, позволяет контролировать обновление вызовов метода.

Операция Remove Parameters

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

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

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

На рисунке ниже показано диалоговое окно Remove Parameters, которое используется для удаления параметров из списка параметров. Если параметр был удален непреднамеренно, его легко восстановить.

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

Для того чтобы оценить внесенные изменения, можно снова использовать окно предварительного просмотра.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

Операция Rename

Система Visual Studio 2013 обеспечивает переименование переменных, методов и классов как в языке C#, так и в языке VB. Диалоговое окно Rename для языка C# приведено на рисунке ниже; оно довольно похоже на соответствующее окно для языка VB, хотя в нем нет функции поиска комментариев или строк.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

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

Операция Generate Method Stub

Когда разработчик пишет код, он может прийти к выводу, что ему нужен вызов метода, который еще не написан. Например, следующий сниппет иллюстрирует новый метод, который должен быть сгенерирован на более поздней стадии:

public void MethodA()
{
string InputA;
double InputB;
int OutputC = NewMethodIJustThoughtOf(InputA, InputB);
}

Разумеется, этот код порождает ошибку при сборке, поскольку вызываемый метод еще не определен. Используя операцию рефакторинга Generate Method Stub (доступную с помощью интеллектуального указателя в самом коде), можно сгенерировать заглушку метода. Как видно из следующего примера, заглушка метода содержит входные параметры и тип возвращаемого значения:

private int NewMethodIJustThoughtOf(string InputA, double InputB)
{
throw new Exception(«The method or operation is not implemented.»);
}

Очень полезно поддерживать упорядоченный список директив using в каждом файле (на языке C#) и ссылаться только на те пространства имен, которые действительно необходимы в данном файле. Команда Organize Usings, доступная в контекстном меню, которое открывается после щелчка пользователем правой кнопкой мыши в окне редактора кода, поможет в обоих этих случаях:

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

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

Чтобы определить, какие из этих директив используются, а какие нет, вместо метода проб и ошибок можно выполнить операцию, предусмотренную системой Visual Studio.

Для этого достаточно щелкнуть правой кнопкой мыши в окне редактора кода и выбрать команду Organize Usings —> Remove Unused Usings (в языке C#). Неиспользуемые директивы using, их альтернативные имена и внешние альтернативные имена сборок из исходного файла будут удалены.

Разработчики проектов на языке VB не имеют возможности упорядочивать и удалять неиспользуемые инструкции Imports.

Однако на закладке References в диалоговом окне Project Properties можно пометить пространства имен, которые должны быть импортированы в каждый исходный файл.

Это позволит значительно сократить количество инструкций Imports. На этой странице можно также удалить ссылки на неиспользуемые сборки.

Очень полезно упорядочивать директивы using в алфавитном порядке, чтобы легко управлять пространствами имен, на которые они ссылаются. Для того чтобы не делать это вручную, нужно щелкнуть правой кнопкой мыши в окне редактора кода и выбрать команду Organize Usings —> Sort Usings.

Если для пространству имен задано альтернативное имя, то оно переносится в конец списка, а если в проекте используются альтернативные имена внешних сборок (с помощью ключевого слова extern в языке C#), то они перемещаются в начало списка.

Читайте также:  Промт-инженер: как правильно писать промты для нейросетей

Для того чтобы одновременно упорядочить директивы using и удалить неиспользуемые, достаточно щелкнуть правой кнопкой мыши в окне редактора кода и выбрать команду Organize Usings —> Remove and Sort.

Файлы шаблонного кода, предусмотренные в системе Visual Studio по умолчанию, содержат директивы using в начале файла за пределами блока пространства имен. Однако, согласно принципам стиля StyleCop, директивы using должны содержаться в блоке пространства имен. Операция Organize Usings распознает текущее расположение инструкций using в файле и оставляет их на месте.

10 Visual Studio Code расширений для рефакторинга и работы с tech debt

В этой статье мы поговорим о полезных extensions для VS Code, которые помогут вам с рефакторингом вашего проекта и работой с тех долгом

1. Glean

Скачать можно тут.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

Glean предоставляет инструменты рефакторинга проектов на React. Основные фичи:

  • Позволяет извлекать JSX в новый компонент
  • Позволяет преобразовывать компоненты класса в functional компоненты и наоборот
  • Позволяет «врапнуть» JSX c conditional
  • Позволяет одновременно переименовывать переменные состояния и их «сеттеры».
  • Позволяет враппить код с помощью useMemo, useCallback или useEffect
  • Перемещение кода между файлами
  • Поддержка TypeScript
  • Поддержка модулей ES2015
  • Поддержка модулей CommonJS

2. Stepsize

Stepsize будет полезен для команд, чтобы отслеживать и определять приоритеты тех долга прямо в VS Code.

Скачать можно тут.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

Что может плагин:

  • Позволяет создавать и просматривать issues прямо из редактора
  • Отслеживать и определять приоритеты код импрувментов, тех долга
  • Добавлять ключевые issue'сы в свои спринты с помощью интеграции с Jira
  • Так же есть интеграции с GitHub, BitBucket, Slack

3. P42 JavaScript Assistant: Refactoring Hints & Automation

P42 JavaScript Assistant добавляет 49 автоматических рефакторингов, и других экшинов для JavaScript и TypeScript в VS Code. 

Скачать можно тут.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

4. Abracadabra

Скачать можно тут.

В VS Code уже есть базовые функции рефакторинга. Abracadabra расширяет возможности рефакторинга в vs code:

  • ???? Больше рефакторинга чем в стандартном пакете vs code
  • ⚡ Shortcuts, чтобы мгновенно запускать самые полезные
  • ???? Quick Fixes, предлагающие рефакторинг, когда это необходимо
  • ???? Options для настройки UX в соответствии с вашими потребностями
  • ???? Рефакторинг для файлов .js, .jsx, .ts, .tsx и .vue

5. TODO Highlight

Если вы любите юзать в коде теги TODO и FIXME, но при этом забиваете на них, этот плагин решит вашу проблему. Он выделяет в коде фразы TODO или FIXME.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

Скачать можно тут.

6. Todo Tree

Расширение Todo Tree собирает все ваши todoшки и fixme, разбросанные по коду, в одно дерево в левой панели vs code

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

Скачать можно тут.

7. Comment Anchors

Comment Anchors позволяют размещать «якоря» в х. Якоря можно использовать для трекинга TODO, написания заметок, упрощать навигацию по файлам.

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

Скачать можно тут.

8. New Relic CodeStream

New Relic CodeStream — это платформа для совместной работы разработчиков, которая интегрирует основные инструменты разработки в VS Code. Помогает избежать переключение контекста и упрощает обсуждение кода и проверку кода, с помощью инструментов для совместной работы в vs code.

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

У экстеншина есть несколько интеграций:

  • Code Hosts: Bitbucket, Bitbucket Server, GitHub, GitHub Enterprise, GitLab, GitLab Self-Managed
  • Issue Trackers: Asana, Azure DevOps, Bitbucket, Clubhouse, GitHub, GitHub Enterprise, GitLab, GitLab Self-Managed, Jira, Linear, Trello, YouTrack
  • Observability: New Relic One, Pixie
  • Messaging Services: Slack, Microsoft Teams

Скачать  New Relic CodeStream можно тут.

9. SonarLint

SonarLint позволяет исправлять проблемы кодом до того, как они обнаружаться во время релиза, у SonarLint есть такие фичи как: средство проверки орфографии, SonarLint выделяет ошибки и уязвимости безопасности при написании кода с четкими инструкциями по исправлению, чтобы вы могли исправить их еще до того, как вы закоммитите код. SonarLint в VS Code поддерживает анализ кода JavaScript, TypeScript, Python, Java, HTML и PHP.

Скачать можно тут.

10. Code Runner

Позволяет запустить сниппет или код на многих языках, таких как: C, C++, Java, JavaScript, PHP, Python, Perl, Perl 6, Ruby, Go, Lua, Groovy, PowerShell, BAT/CMD, BASH/SH, F# Script, F# (.NET Core), C# Script, C# (.

NET Core), VBScript, TypeScript, CoffeeScript, Scala, Swift, Julia, Crystal, OCaml Script, R, AppleScript, Elixir, Visual Basic .

NET, Clojure, Haxe, Objective-C, Rust, Racket, Scheme, AutoHotkey, AutoIt, Kotlin, Dart, Free Pascal, Haskell, Nim, D, Lisp, Kit, V, SCSS, Sass, CUDA, Less, Fortran, Ring.

Скачать Code Runner можно тут.

Источник: https://dev.to/alexomeyer/10-vs-code-extensions-to-fight-technical-debt-508f

8 рекомендаций по написанию читаемого кода на C# с помощью .NET 6

Правильный рефакторинг в C#: улучшаем читабельность кода с помощью высокой связности

Как говорит гуру программирования Роберт К. Мартин, несмотря на то, что чистый код может быть не идеален в своем определении, его результаты отчетливо понятны. Вы наверняка предпочтете, чтобы ваш код был читаем, подобно хорошей книге, и без проблем расширялся. Ошибки либо должны быть легко обнаружимыми, либо избегаться ввиду самой структуры программы. Чистый код позволяет поддерживать продуктивность на высоте и одновременно снижает затраты на обслуживание.

Вот цитата из книги “Чистый код. Создание, анализ, рефакторинг”:

“Чистый код может читать и улучшать любой разработчик, а не только его изначальный автор”,  —  Дэйв Томас.

Это одна из важнейших характеристик чистого кода. Если код можете обслуживать не только вы, но и любой другой разработчик близкого к вам уровня, то такой он автоматически впишется в большинство требований звания “чистый”. Естественно, он может быть не на все 100% таковым, но достаточно, чтобы он соответствовал свойствам, описанным в начале.

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

Так что же делает код более читаемым?

В этой статье мы не будем разбирать методы, которые описаны в книге “Чистый код. Создание, анализ, рефакторинг” —  с ними вы можете ознакомиться самостоятельно. Вместо этого я сосредоточусь на конкретных рекомендациях, которые выработал за время работы с .NET в течение последних 8 лет.

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

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

//Плохоpublic void Test(Test test) { if (test == null) { throw new ArgumentNullException(nameof(test)); } else { //Do something }}//Хорошоpublic void Test(Test test) { if (test == null) throw new ArgumentNullException(nameof(test)); //Нужные действия}// ____________________//Плохоpublic Result Test(SomeEnum someEnum) { if (someEnum == SomeEnum.A) { //Do something } else { //Do the Other thing }}//Хорошоpublic Result Test(SomeEnum someEnum) { return someEnum switch { SomeEnum.A => DoSomething(), SomeEnum.B => DoTheOtherThing(), _ => throw new NotImplementedException() }; Result DoSomething() { } Result DoTheOtherThing() { }

}

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

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

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

interface IRequestHandler{ Result Handle();}internal class Test1 : IRequestHandler{ public Result Handle() { //Действия }}internal class Test2 : IRequestHandler{ public Result Handle() { //Прочие действия }}public Result HandleRequest(IRequestHandler requestHandler)

=> requestHandler.Handle();

В примере avoid_else.cs можно заметить и еще одно отличие, вносимое исключением else. Вы избегаете в методе одного уровня отступов. Хорошей практикой для повышения читаемости кода будет просто избежание лишних уровней отступов. Я на собственном опыте обнаружил, что лучше всего придерживаться трех-четырех. При этом они должны быть поистине необходимыми.

Например, никогда не следует вкладывать ветку if в другую ветку if. По правде говоря, if вообще лучше стараться избегать.

Практически единственный случай, в котором у меня получается более одного уровня отступов  —  это использование запросов LINQ.

Читайте также:  Как понять, что пора увольняться

Вам мог встречаться подобный код:

//Плохоpublic List Test(string[] someStrings) { List listToFill = new(); foreach (var s in someStrings) { listToFill.Add(s.ToUpper()); } return listToFill;}//Хорошоpublic List Test(IEnumerable someStrings) => someStrings .Select(s => s.ToUpper())

.ToList();

Спросите себя: “Можно ли заменить цикл запросом Linq?”. Гарантирую, что в 90% случаев ответом будет: “Да”. Это может быть моим личным мнением, но я считаю, что хороший запрос Linq гораздо понятнее масштабного цикла for.

Чаще всего при использовании таких циклов вы собираетесь преобразовать один или более классов Enumerable в результат. В Linq есть все необходимое для применения проверок, трансформаций и действий для IEnumerable. Если вы с этим языком еще не работали, то настоятельно его вам рекомендую.

В Linq у вас есть выбор между синтаксисом запросов и синтаксисом методов. Лично я предпочитаю последний, но тут уже дело вкуса. Все расширяющие методы для IEnumerable можно найти здесь.

Извлечение методов и присваивание им понятного имени также сильно повышает читаемость.

//Плохоpublic List Test(IEnumerable someStrings) => someStrings .Where(s => s.StartsWith(«a», StringComparison.OrdinalIgnoreCase)) .Select(s => s.Replace(«a», «»)) .ToList(); //Хорошо(Представьте, что здесь используется более сложная логика)public List Test(IEnumerable someStrings) => someStrings .Where(StartsWithA) .Select(RemoveA) .ToList();private static bool StartsWithA(string s) => s.StartsWith(«a», StringComparison.OrdinalIgnoreCase);private static string RemoveA(string s)

=> s.Replace(«a», «»);

Представьте, что в методах Where() или Select() используется более сложная логика, чем в данном простом примере.

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

Еще один способ повышения читаемости кода  —  это правильное его разделение и использование отступов для строк. В большинстве случаев правильно расставить отступы вам поможет Visual Code, хотя тоже не идеально.

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

  • Строки кода не должны выступать за середину экрана. Это упростит его чтение и позволит использовать разделение экрана.
  • Уровень отступов открывающих и закрывающих скобок должен совпадать. Я знаю, что это займет несколько дополнительных секунд, но раскрытие аргументов и добавление им имен упрощает чтение метода и его редактирование (см. второй пример).
  • Делать разрыв перед точкой или после запятой. Таким образом, вы можете удалить целую строку для перемещения части кода.
  • Каждая лямбда-функция должна отделяться дополнительным уровнем отступа .

Разрыв перед точкой:

//Плохоpublic Task BlobExistsAsync(string containerName, string blobName) => client.GetBlobContainerClient(containerName).GetBlobClient(blobName).ExistsAsync().GetValueAsync();//Хорошоpublic Task BlobExistsAsync(string containerName, string blobName) => client .GetBlobContainerClient(containerName) .GetBlobClient(blobName) .ExistsAsync()

.GetValueAsync();

Разрыв аргументов и добавление имен:

public void Test() { //Плохо var test1 = new Test(«a», «b», «c», «d», «e»); //Хорошо var test2 = new Test( argument1: «a», argument2: «b», argument3: «c», argument4: «d», argument5: «e» );

}

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

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

public void Test(bool predicate) { //Плохо string s; if (predicate) { s = «Hello World»; } else { s = «Bye World»; } //Хорошо var s = predicate ? «Hello World» : «Bye World»;

}

  • Краткие проверки на null:

public void Test() { //Проверяет левое значение на null, и если это так, использует //правое значение; var a = null; var b = a ?? new Xyz(); //Выбрасывает исключение, если значение null var c = a ?? throw new Exception(); //Если d равно null, создает new D(); var d = null; d ??= new D();

}

  • А вот, на мой взгляд, самая краткая интерполяция строк:

public void Test() { var a = «Name» var s = $»Hello {a}» // s — это «Hello Name»

}

  • Сопоставление шаблонов. По началу покажется сложным, но в итоге окажется самым удобочитаемым механизмом различения кейсов:

public void Test() { var a = 1; var b = a switch { 1 => «a is 1», 2 => «a is 2», _ => «a is not 1 or 2» } // b = «a is 1»

}

  • Тела выражений. Лучший вариант для однострочных функций или свойств:

public string Test(bool predicate) { return predicate ? «true» : «false»;}public string Test(bool predicate)

=> predicate ? «true» : «false»;

  • Типы записей. Лучше всего подходят для Plain Old Class Object (POCO):

//Старыйpublic class Xyz() { public string Test { get; } public string Test2 { get; } public Xyz(string test, string test2){ Test = test; Test2 = test2 }}//Новый

public record Xyz(string Test, string Test2);

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

В C#10 появилось две интересных возможности: file-scoped namespaces и global usings.

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

//Старый вариантnamespace This.Is.A.Test.Namespace { public class Test { }}//Новый вариантnamespace This.Is.A.Test.Namespace; public class Test {

}

Global usings работают в масштабе проекта и экономят кучу места в начале файлов. Некоторые usings окажутся очень полезны для последующих разработчиков, помогая понять, на какие пакеты и пространства имен вы ссылаетесь в коде, тем не менее, использовать их нужно осторожно. Добавляйте global usings только для тех пространств имен, которые используются в проекте часто.

Результат global usings и implicit usings:

//Старый вариантusing System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace xyz;class Abc {}//Новый вариантnamespace xyz;class Abc {

}

  • В этом примере usings также можно импортировать, включив в проекте функционал implicit usings.
  • Читайте нас в Telegram, VK и Яндекс.Дзен
  • Перевод статьи Tobias Streng: 8 Guidelines to Write Readable Code in C# With .NET 6

Рефакторинг кода на языке C#

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

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

На этом тренинге слушатели получат представление:

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

После тренинга слушатели будут уметь:

  • Выделять проблемные места в программном коде на языке C#.
  • Улучшать читабельность исходного кода на языке C#.

Разработчики исходного кода на языке С# с опытом работы от 1 года до 2 лет.

  • Базовые общие знания в области программирования
  • Базовые знания скриптовых языков
  • Способность понимать логические потоки кода
    • Понятие рефакторинга, для чего нужен рефакторинг кода;
    • Разработка через тестирование.
    • Общая информация;
    • Определение запаха кода;
    • Принципы хорошего дизайна;
    • Запахи кода в тестах, запахи в базах данных (обзор);
    • Запахи в архитектуре, запахи в управлении персоналом.
    • Организация данных;
    • Поля;
    • Массивы данных;
    • Ссылки и значения;
    • Кодовые поля;
    • Упрощение условных выражений;
    • Упрощение вызова методов;
    • Работа с обобщениями;
    • Композиция методов.
    • Легко обнаруживаемые проблемы;
    • Именование;
    • Чрезмерная сложность;
    • Дублирование;
    • Условная логика.
    • Принцип единой ответственности;
    • Связность;
    • Закон Деметра;
    • Передвижение методов/полей;
    • Выделение класса;
    • Встраивание класса;
    • Сокрытие делегатов;
    • Удаление посредника;
    • Методы расширения.
    • Данные;
    • Наследование;
    • Ответственность;
    • Приспосабливающиеся изменения;
    • Библиотечные классы.

Улучшите читаемость кода с помощью шаблона Fluent Builder в C#

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

Читайте также:  Инкапсуляция и наследование в Python на примерах: код и задачи

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

Теперь у нас есть разные варианты создания экземпляра этого класса, т. е. конструктор, инициализатор объекта или кусочное создание.

Инициализация через конструктор

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

Инициализация с помощью инициализатора объектов

По сравнению с конструктором инициализатор объекта явно указывает имена полей и значения переданных аргументов. Это делает более явным то, что происходит.

Этот подход совершенно удобен, если все ваши поля имеют общедоступные сеттеры, что означает, что ваш объект не является неизменным. В C# 9.

0 появилось ключевое слово init, которое является специализированным сеттером, позволяющим устанавливать значение поля с помощью инициализатора объекта.

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

Строитель

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

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

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

Пример

Следующий класс иллюстрирует пример шаблона построителя.

Инициализация объекта будет выглядеть следующим образом.

Свободный строитель

Расширение шаблона построителя с помощью API Fluent упрощает чтение оператора и позволяет вам связать операторы в цепочку, чтобы получить желаемую конфигурацию объекта. Больше нет необходимости указывать объект построителя в каждом операторе.

Пример

Следующий фрагмент ниже показывает, как его использовать.

Выводы и ограничения

Шаблон построителя — отличный способ создания экземпляров классов. Лично я использую этот шаблон в основном для тестирования.

Это простой способ создания фиктивных объектов, четко указывающих настройку теста для конкретного сценария.

Другим практическим примером является использование его для пользовательских HttpClients, которые могут быть созданы с различными (предварительно) конфигурациями.

Несмотря на преимущества, которые он дает, в fluent Builder включить наследование довольно сложно. Вам нужно будет использовать рекурсивные дженерики. Этот подход работает, однако делает код компоновщика более сложным.

Что такое рефакторинг кода и зачем он нужен

Рефакторинг — это переработка исходного кода программы, чтобы он стал более простым и понятным.

https://www.youtube.com/watch?v=AkdEsCHt1cg\u0026pp=ygWbAdCf0YDQsNCy0LjQu9GM0L3Ri9C5INGA0LXRhNCw0LrRgtC-0YDQuNC90LMg0LIgQyM6INGD0LvRg9GH0YjQsNC10Lwg0YfQuNGC0LDQsdC10LvRjNC90L7RgdGC0Ywg0LrQvtC00LAg0YEg0L_QvtC80L7RidGM0Y4g0LLRi9GB0L7QutC-0Lkg0YHQstGP0LfQvdC-0YHRgtC4

Рефакторинг не меняет поведение программы, не исправляет ошибки и не добавляет новую функциональность. Он делает код более понятным и удобочитаемым.

Например, вот фрагмент на Python, создающий список из строки:

list = []
for char in 'abcdef': if char != 'c': list.append(char * 2)
print(list) # ['aa','bb','dd','ee','ff']

При рефакторинге его можно упростить, применив конструктор списков:

list = [char * 2 for char in 'abcdef' if char != 'i']
print (list)

Результат работы программы не изменился, но код стал проще, компактнее и понятнее.

Последовательность таких небольших изменений может сильно улучшить качество проекта.

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

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

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

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

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

Поэтому даже идеальная когда-то программа со временем требует нового рефакторинга, обновляющего устаревшие участки кода.

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

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

Цель оптимизации — улучшение производительности программы, а рефакторинга — улучшение понятности кода. После оптимизации исходный код может стать сложнее для понимания.

После рефакторинга программа может начать работать быстрее, но главное — её код становится проще и понятнее.

Признаки, показывающие, что назрела необходимость в рефакторинге:

  • Программа работает, но даже небольшие доработки сильно затягиваются из-за того, что каждый раз приходится долго разбираться в коде.
  • Разработчик постоянно не может точно сказать, сколько времени ему нужно на выполнение задачи, потому что “там надо вначале разбираться”.
  • Одинаковые изменения приходится вносить в разные места текста программы.

Такой код нужно срочно рефакторить, иначе он будет тормозить реализацию проекта и затруднять внесение правок.

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

Рефакторинг — это маленькие последовательные улучшения кода. Чистить можно всё, но в первую очередь найдите эти проблемы:

  1. Мёртвый код. Переменная, параметр, метод или класс больше не используются: требования к программе изменились, но код не почистили. Мёртвый код может встретиться и в сложной условной конструкции, где какая-то ветка никогда не исполняется из-за ошибки или изменения требований. Такие элементы или участки текста нужно удалить.
  2. Дублирование. Один и тот же код выполняет одно и то же действие в нескольких местах программы. Вынесите эту часть в отдельную функцию.
  3. Имена переменных, функций или классов не передают их назначение. Имена должны сообщать, почему элемент кода существует, что он делает и как используется. Если видите, что намерения программиста непонятны без , — рефакторьте.

    Примеры корректных имен: totalScore — переменная, означающая итоговый счёт в игре, maxWeight — максимальный вес. Для функций и методов лучше использовать глаголы, например: saveScore () — сохранить счет, setSize () — задать размер, getSpeed () — получить скорость.

  4. Слишком длинные функции и методы. Оптимальный размер этих элементов — 2-3 десятка строк. Если получается больше, разделите функцию на несколько маленьких и добавьте одну общую. Пусть маленькие выполняют по одной операции, а общая функция их вызывает.
  5. Слишком длинные классы. То же самое. Оптимальная длина класса — 20–30 строк. Разбейте длинный класс на несколько маленьких и включите их объекты в один общий класс.
  6. Слишком длинный список параметров функции или метода. Они только запутывают, а не помогают. Если все эти параметры действительно нужны, вынесите их в отдельную структуру или класс с понятным именем, а в функцию передайте ссылку на него.
  7. Много комментариев. Плохой код часто прикрывается обильными ми. Если почувствовали желание пояснить какой-то участок кода, попробуйте сначала его переписать, чтобы и так стал понятным. Бесполезные комментарии загромождают программу, а устаревшие и неактуальные вводят в заблуждение.

После каждой правки посмотрите на соседние участки кода: возможно, их тоже стоит поправить и сделать понятнее. И на те участки кода, которые давно не редактировались, — они уже могли стать некорректными.

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

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

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

Не страдайте перфекционизмом! Если вы поправили какой-то кусочек кода, не надо перетряхивать всю программу, разыскивая, что ещё можно улучшить. Стремление к совершенству вечно, но лучше обойтись без фанатизма.

Мы всё-таки меняем рабочий код. Тут можно не только всё упростить, но и сильно напортачить. Небрежный рефакторинг может отбросить выполнение проекта на дни и недели.

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

Рефакторьте постоянно и по чуть-чуть.

  • Иногда бывают злоупотребления: рефакторинг может стать способом саботажа, отговоркой, с помощью которой откладываются важные релизы и внедрение новых фич.
  • Но всё равно нельзя пренебрегать усовершенствованием кода, потому что это лучший способ ускорить работу в будущем.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *