Результати пошуку за запитом: design pattern
Bottom NavigationView
Автор: Євген Мица
Цей урок розглядає порядок формування Bottom NavigationView за допомогою Android Design Support Library, що дозволяє додати додаткову функціональність додатку Android через налаштування нижньої навігаційної панелі.
Що таке патерни проєктування у програмуванні
Автор: Влад Сверчков
Що таке патерн (шаблон) проєктування.
Коли використовують шаблони.
Якими бувають патерни проєктування.
Породжуючі.
Структурні.
Патерни поведінки.
Як обрати шаблон?
Висновки.
Програмісти-початківці завжди приходять до точки, коли їхній код перетворюється на “спагеті”. Його важко читати, він містить масу самоповторень, зайвих функцій, а додавання нового функціоналу перетворюється на десяте коло пекла.
Один із найкращих засобів запобігання цьому – використовувати патерни проєктування (Design Patterns). Чи є це срібною кулею, які переваги та недоліки патернів існують, і які з них необхідно знати розробникам? Відповіді розбираємо нижче.
Що таке патерн (шаблон) проєктування?
Патерни – це типові архітектурні рішення проблем, котрі часто зустрічаються під час розроблення ПЗ. Їхня інша назва – шаблони, і що цікаво – людство дуже часто оточує себе шаблонами у повсякденному житті:
однакові гнізда розетки та форми вилок у приміщеннях – універсальне рішення для електроживлення;
виделки та ложки – інструменти споживання майже будь-якої їжі;
чашки – ємності для розміщення будь-якої рідини і так далі.
Людина завжди прагне спростити традиційну діяльність, і це не могло обійти стороною програмування.
Ідеї створення універсальних правил для якісної розробки існували ще до 90-х років минулого століття, але дійсно проривною стала праця "Design Patterns: Elements of Reusable Object-Oriented Software" (1994) авторства Еріха Ґамма, Річарда Гелма, Ральфа Джонсона та Джона Вліссідеса, які іменують себе як "Банда чотирьох" (Gang of Four, GoF).
У книзі описано 23 патерна та їхнє застосування в об'єктно-орієнтованому дизайні. Ця праця стала фундаментальною і тепер патерни gof складають кістяк багатьох обговорень якісного коду.
Коли використовують патерни
В розробці шаблони використовують при необхідності приведення коду до наступних критеріїв:
Читабельність – інші розробники мають без складнощів розуміти написане.
Масштабованість – легкість у створенні нового функціоналу.
Підтримуваність – оновлення кодової бази має проходити якомога плавніше.
Також вони здатні підвищити швидкість і продуктивність розробника – патерни це дійсно дозволяють. Вони гарно справляються і з наступними задачами:
зменшення кількості потенційних помилок та вузьких місць;
спрощення рефакторингу;
зменшення технічного боргу;
покращення комунікації девелоперів з іншими програмістами, проєктними менеджерами, власниками тощо.
Необхідність використати шаблони проектування зростає разом зі збільшенням кодової бази, особливо при комерційному розробленні – коли створюване ПЗ має приносити прибуток.
Важливо пам’ятати, що використання патернів інколи є геть недоречним. Подекуди воно може значно ускладнити читабельність, громіздкість і масштабованість коду. Наприклад, нескладний функціонал, який нечасто використовується і займає мало місця в коді, не потребує pattern-втручання. А от репетативний код, що вирішує класичні задачі (сортування, перебір даних тощо) – ідеальний претендент на застосування шаблону.
Аби не помилитися спершу з’ясуйте контекст вашої проблеми, а вже потім обирайте патерни програмування, які найкраще задовольняють вимогам.
Якими бувають патерни проєктування
У своїй книзі GoF виділяють три великі сімейства:
Сімейство
Короткий опис
Породжуючі патерни або Creational Patterns
Надають найкращі способи створення об'єктів. Вони абстрагуються від процесу конкретизації і роблять вашу систему незалежною від створення, компонування та представлення її об'єктів.
Популярні приклади: “Абстрактна фабрика” (Abstract Factory), “Одинак” / “Одиночка” (Singleton), “Прототип” (Prototype), “Фабричний метод” (Factory Method).
Структурні патерни або Structural Patterns
Фокусуються на композиції об’єкту. Допомагають переконатися в тому, що зміна частини системи не потягне за собою необхідність змін в інших її складових.
Популярні приклади: “Проксі” (Proxy), “Адаптер” (Adapter), “Компонувальник” (Composite), “Фасад” (Facade).
Патерни поведінки або Behavioral Patterns
Зона відповідальності – алгоритми та обмін інформацією між об’єктами.
Популярні приклади: “Відвідувач” (Visitor), “Ітератор” (Iterator), “Ланцюжок обов’язків” (Chain of Responsibility), “Стратегія” (Strategy).
Розглянемо більш детально деякі з них.
Породжуючі
Породжуючі патерни – це надійні помічники у створенні об’єктів таким чином, аби в майбутньому з ними було максимально легко працювати.
Дамо короткий опис деяких шаблонів:
Патерн Одинак / Сінглтон забезпечує наявність лише одного екземпляру класу з глобальною точкою доступу. Singleton поширений в задачах конфігурацій або логування в застосунках, де потрібен єдиний контрольований доступ.
Шаблон Прототип дозволяє створювати нові об'єкти шляхом копіювання існуючих екземплярів. Використовується Prototype в ситуаціях, коли створення об'єкта надто дороге, наприклад, при клонуванні складних або ресурсоємних об'єктів.
Фабричний метод визначає інтерфейс для створення об'єктів, але дозволяє підкласам самостійно визначати тип створюваних об'єктів. Fabric Method корисний у багатофункціональних застосунках, де класи повинні мати можливість вибирати тип об'єктів, наприклад, при роботі з різними форматами документів, системами онлайн платежів тощо.
Абстрактна фабрика визначає інтерфейс для створення сімейств пов'язаних об'єктів без вказівки їх конкретних класів. Використовують Abstract Factory для створення різних компонентів інтерфейсу користувача, які повинні працювати разом і забезпечувати єдиний стиль (світла / темна тема вебсайту тощо).
Розглянемо приклад на патерні Singleton. Уявіть собі просту програму – музичний плеєр. Він дозволяє користувачам відтворювати музичні файли. Однак водночас має працювати лише один екземпляр плеєра – можливість відкриття декількох одночасно повинна бути недоступна. Цього можна досягти за допомогою шаблону Singleton.
Простий приклад коду мовою C#:
public class MusicPlayer
{
private static MusicPlayer _instance;
private MusicPlayer()
{
// Ініціалізуємо музичний плеєр (наприклад, завантажуємо плейлисти)
}
public static MusicPlayer Instance
{
get
{
if (_instance == null)
{
_instance = new MusicPlayer();
}
return _instance;
}
}
public void PlaySong(string songPath)
{
// Запустити пісню
}
public void PauseSong()
{
// Поставити на паузу
}
public void StopSong()
{
// Зупинити відтворення пісні
}
}
// Отримуємо екземпляр MusicPlayer
MusicPlayer player = MusicPlayer.Instance;
// Використовуємо функціонал MusicPlayer
player.PlaySong("C:\\Users\\yourUsername\\Music\\mySong.mp3");
player.PauseSong();
player.StopSong();
Щоразу як в різних ділянках проєкту вам треба буде створювати екземпляр плеєру для відповідної взаємодії, ви завжди працюватимете лише з одним і тим самим екземпляром, уникаючи дублікації.
Якщо ви програмуєте мовою сі шарп, детально розібрати популярні патерни проєктування C# з прикладами ви можете за посиланням.
Структурні
З короткого опису в таблиці легко дійти висновку, що структурні патерни дозволяють сформувати надійну, масштабовану та підтримувану архітектуру проєкту. Коротке знайомство:
Проксі забезпечує об'єкт-посередник для контролю доступу до іншого об'єкта. Зазвичай шаблон Proxy використовують для реалізації “лінивого” завантаження, коли об'єкт створюється або ініціалізується лише при зверненні до нього (наприклад, завантаження картинок з високою роздільною здатністю).
Адаптер дозволяє об'єктам з несумісними інтерфейсами працювати разом. Застосовується патерн Adapter для інтеграції нових компонентів в існуючу систему без зміни її коду. Підходить для використання нової бібліотеки у старому застосунку.
Компонувальник використовується для ієрархічного компонування об'єктів для подальшої роботи з ними як з єдиним об'єктом. Використовується для створення деревоподібних структур, як-от файлові системи або GUI, де кожен вузол може бути як простим, так і Composite об'єктом.
Фасад (Facade) надає спрощений інтерфейс для взаємодії зі складною системою або набором класів. Він зменшує складність роботи з підсистемами і надає користувачам єдиний вхідний інтерфейс для виконання рутинних операцій.
Вивчити саме структурні патерни проєктування C# (з прикладами) ви можете за посиланням.
Поведінкові
Патерни поведінки в першу чергу визначають зв’язки між об’єктами і те, як вони здійснюють обмін інформацією. Наприклад:
Патерн Відвідувач (Visitor) дозволяє додавати нові операції до об'єктів без зміни їхніх оригінальних класів. Використовується для взаємодії з об’єктами зі складною структурою, коли внесення додаткової логіки в оригінальні класи невиправдано ускладнює код.
Ітератор / Iterator надає зручний механізм послідовного та простого доступу до елементів колекції, незважаючи на складність її побудови. Даний патерн поведінки популярний при обході елементів контейнерів, як-от списки або масиви – він надає універсальний інтерфейс для різних типів колекцій.
Ланцюжок обов’язків або ж патерн Chain of Responsibility дозволяє передавати запит ланцюжком обробників, поки один з них не обробить запит. Незамінний при обробці запитів на сервері, де кожен обробник може передати запит наступному обробнику в ланцюжку: перевірка при авторизації на сайті, оброблення подій у GUI тощо.
Для входу в патерни проєктування книга від Gang of Four буде гарною точкою відліку. Ви познайомитеся з класикою та академічним розкриттям теми, використовуючи патерни gof. Якщо ж ви хочете збагатити свої знання шаблонів, але віддаєте перевагу мові Java, рекомендуємо відео курс “Патерни проектування Java”.
Як обрати патерн?
Спочатку ви маєте проаналізувати задачу – для більшої зрозумілості виконайте її декомпозицію, розбивши на декілька складових. При цьому використовуйте системний підхід: прорахуйте, як ваше рішення вплине на весь проєкт, які елементи воно зачепить зараз, і який вплив воно матиме на додавання нового коду.
Якщо ви вже працюєте в ІТ-компанії, ваші колеги, тімлід або архітектор можуть підказати вам доцільність використання того чи іншого патерну, розкрити нюанси вже існуючої архітектури, кодового стилю та багато іншого.
Лише після ретельного аналізу можна переходити до підбору шаблону, зважаючи на усі переваги та недоліки. До речі, в цих задачах гарними помічниками будуть безкоштовні AI-асистенти на кшталт ChatGPT, Gemini та ін.
Також не забувайте про використання інших методик покращення кодової читабельності, масштабування й чистоти:
SOLID принципи – вони регламентують 5 основних засад створення структурованого, якісного коду. Нещодавно ми проводили вебінар, на якому розбирали кожен принцип в деталях, запрошуємо до перегляду! А якщо вас цікавить прикладний характер SOLID принципів на Java, можете пройти даний відео курс.
GRASP (General Responsibility Assignment Software Patterns) – патерни для об’єктно-орієнтованого проєктування. Вони не мають вираженої структури і носять більш абстрактний характер, аніж патерни gof.
DRY (Don’t Repeat Yourself) – головна ідея даного принципу полягає у створенні коду, який не матиме дублікацій в проєкті.
KISS (Keep It Simple, Stupid) – регламентує написання якомога простішого коду, аби його можна було легко читати і розуміти.
Рефакторинг – повернення до вже написаного коду з метою його покращення без зміни функціональності.
Інші техніки, що залежать від проєктів.
Висновки
Патерни грають ключову роль в сучасному розробленні. Вони акумулюють в собі найкращі практики створення кодової бази таким чином, аби досягнути максимальної легкості та ефективності розроблення, особливо на великих проєктах.
Звісно, не завжди їхнє використання є доречним – потрібно аналізувати задачі і продумувати наслідки застосування того чи іншого шаблону, аби не отримати величезну валізу без ручки.
Розвивайте вашу експертизу в області патернів – це win-win стратегія. З одного боку перед працедавцями ви постанете як досвідчений та висококваліфікований спеціаліст, а з іншого – ваші програмні рішення матимуть елегантний характер і відзначатимуться легкістю в читанні, підтримці та масштабуванні.
Чи використовуєте ви патерни в своїй розробницькій діяльності? Можливо, тільки вивчаєте? Залишайте в коментарях ваші відповіді!
Mobile Challenge
Автор: Редакция ITVDN
Стартовала регистрация на Mobile Challenge - первый Всеукраинский чемпионат по мобильной разработке.
Сотни программистов со всех регионов Украины соберутся, чтобы проверить себя, найти новые возможности и знакомства.
Номинации:
IOS
Android
Mobile Design
Team
Регистрируйтесь прямо сейчас
Генеральные партнеры: NIX Solutions, Edsson.
Партнеры: Lohika, Global Logic, Ciklum.
Списки, древовидные списки, таблицы
Автор: Вадим Рибалко
Данный урок посвящен теме списков. Вы узнаете, что такое списки, их виды и стилизация в Material Design. Рассмотрим обычные списки (lists), древовидные списки (tree lists) и списки сеткой (grid lists). Во второй части урока будем работать с таблицами данных.
Меню и панель инструментов
Автор: Вадим Рибалко
Тема урока – «Меню и панель инструментов». Вы узнаете, что такое меню, ряд полезных свойств меню. Также мы познакомимся с панелью инструментов и применением панели инструментов. Данные знания помогут Вам быстро и качественно работать в стиле Google Material Design.
Tablayout
Автор: Євген Мица
У цьому уроці розглядається порядок формування вкладок (Tabs) за допомогою Android Design Support Library. Tablayout (котейнер вкладок) дозволяє забезпечити горизонтальне компонування відображення вкладок. Вкладки дають можливість перемикатися, здійснювати навігацію між фрагментами екрану.
Practicum
Автор: Анна Дваррі
У цьому уроці докладно розглядається практична частина роботи інженерів із тестування. Наведено практичні завдання та приклади їх виконання за чотирма напрямками, а саме: desktop-testing, web-testing, api-testing, test design. Після закінчення уроку студентам запропонують виконати самостійні роботи для закріплення матеріалу.
DEV Challenge XIX
Автор: Редакция ITVDN
🏆 Найбільший IT-конкурс в Європі DEV Challenge оголошує реєстрацію на 19-й сезон
📍Реєстрація за посиланням: https://ua.devchallenge.it
Обирай свою номінацію:
Backend
Frontend
Manual QA
UI Design
Product Design
iOS Developer
Що ви отримаєте, крім призів:
✅ Змагання допоможуть вам підвищити рівень складності завдань, з якими ви можете впоратись.
✅ 1200+ учасників за сезон. Серед них ви зможете знайти тих, хто вам потрібен: від партнерів по проекту до ментора в потрібній галузі.
✅ Корисний стрес і апгрейд навичок. Досвідченим фахівцям легко впоратися з нетривіальними кейсами. Але на DEV Challenge навіть суперпрофесіоналам доведеться докласти чимало зусиль, щоб досягти успіху.
Нововведення у С# 7.0
Автор: Mads Torgersen
В этой статье мы расскажем о нововведениях в языке C# 7.0, которые были представлены в марте 2017 года как часть релиза Visual Studio 2017.
В C# 7.0 появился целый ряд нововведений и основное внимание уделяется использованию данных, упрощению кода и улучшению производительности. Возможно, самой главной особенностью являются кортежи, которые упрощают получение различных результатов, и сопоставление с шаблоном, что упрощает код, который зависит от формы данных. Существует также множество других нововведений, как значительных, так и не очень. Надеемся, что в совокупности они сделают ваш код более эффективным и точным, а вы при этом будете работать продуктивнее и останетесь довольны результатом.
Если вас интересует процесс разработки, который привел к этому набору функций, вы можете найти заметки, предложения и множество обсуждений на эту тему на сайте C# language design GitHub.
Если данная информация кажется вам знакомой, это только потому, что релиз предварительной версии состоялся в августе прошлого года. В окончательной версии C# 7.0 изменились некоторые детали, некоторые из них - из-за отличных отзывов на указанную ранее статью.
Получайте удовольствие от C# 7.0 и удачного хакинга!
Out переменные
В более ранних версиях C# использование out параметров является не таким легким, как нам хотелось бы. Прежде чем вызвать метод с out параметрами, сначала необходимо объявить переменные, чтобы перейти к нему. Поскольку вы обычно не инициализируете эти переменные (они все равно будут перезаписаны методом), вы также не можете использовать ключевое слово var, но вам нужно указать полный тип:
public void PrintCoordinates(Point p)
{
int x, y; // have to "predeclare"
p.GetCoordinates(out x, out y);
WriteLine($"({x}, {y})");
}
В C# 7.0 мы добавили out переменные, что позволяет объявлять переменную прямо в точке, где она передается как out аргумент:
public void PrintCoordinates(Point p)
{
p.GetCoordinates(out int x, out int y);
WriteLine($"({x}, {y})");
}
Обратите внимание, что переменные находятся в области видимости в окружающем блоке, поэтому последующая строка может их использовать. Многие виды утверждений не устанавливают свою собственную область действия, поэтому out переменные, объявленные в них, часто вводятся в область видимости.
Поскольку out переменные объявляются непосредственно в качестве аргументов для out параметров, компилятор может обычно указывать, каков должен быть их тип (если только не существует конфликтующих перегрузок), поэтому вместо типа можно использовать ключевое слово var:
p.GetCoordinates(out var x, out var y);
Общим использованием out параметров является шаблон Try..., где логическое возвращаемое значение указывает на успех, а out параметры переносят полученные результаты:
public void PrintStars(string s)
{
if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); }
else { WriteLine("Cloudy - no stars tonight!"); }
}
Мы также допускаем «сбрасывание» в качестве out параметров в виде «_», что позволит вам проигнорировать параметры, которые вам не нужны:
p.GetCoordinates(out var x, out _); // I only care about x
Соответствие с шаблоном
В C# 7.0 вводится понятие шаблонов, которые являются синтаксическими элементами, позволяющими проверить соответствие значения определенной «форме» и извлечь информацию из значения, если такое соответствие имеется.
Примеры шаблонов в C# 7.0:
• Константные шаблоны c (где c – константное выражение в C#), которые проверяют, равняется ли переменная этой константе.
• Шаблоны типа T x (где T – тип и x – идентификатор), которые проверяют, имеет ли переменная тип T, и если да, то извлекают значение в новую переменную x типа T.
• Var шаблоны var x (где x – идентификатор), которые всегда совпадают и просто помещают значение ввода в новую переменную x с тем же типом.
Это только начало; шаблоны являются новым типом элемента языка C#, и в будущем мы обязательно добавим новые шаблоны в C#.
В C# 7.0 мы улучшаем две существующие языковые конструкции с шаблонами:
• is теперь может использоваться не только с типом, но и с шаблоном;
• case в операторе switch теперь может использовать шаблоны, а не только константы.
В будущих версиях C#, вероятно, мы добавим больше мест, где можно использовать шаблоны.
Шаблоны с is
Рассмотрим пример использования is с константным шаблоном и шаблоном типа:
public void PrintStars(object o)
{
if (o is null) return; // constant pattern "null"
if (!(o is int i)) return; // type pattern "int i"
WriteLine(new string('*', i));
}
Как видно из примера, переменные шаблона, представленные шаблоном, аналогичны out переменным, описанным ранее, поэтому могут быть объявлены в середине выражения и использоваться в ближайшей окружающей области. Также как out переменные, переменные шаблона изменяемы. Мы часто ссылаемся на out переменные и переменные шаблона совместно как «переменные выражения».
Шаблоны и Try-методы часто используются вместе:
if (o is int i || (o is string s && int.TryParse(s, out i)) { /* use i */ }
Шаблоны с выражениями switch
Мы обобщаем варианты использования switch:
• Вы можете использовать любой тип (не только простые типы).
• Шаблоны могут использоваться в выражениях case.
• Вы можете добавлять дополнительные условия к выражениям case.
Вот простой пример:
switch(shape)
{
case Circle c:
WriteLine($"circle with radius {c.Radius}");
break;
case Rectangle s when (s.Length == s.Height):
WriteLine($"{s.Length} x {s.Height} square");
break;
case Rectangle r:
WriteLine($"{r.Length} x {r.Height} rectangle");
break;
default:
WriteLine("");
break;
case null:
throw new ArgumentNullException(nameof(shape));
}
Существует несколько особенностей, которые следует отметить в этом новом расширенном выражении switch:
• Порядок выражений case теперь имеет значение: как и в случае с выражениями catch, у выражений case выбирается первое по порядку выражение, удовлетворяющее условию. Поэтому важно, чтобы условие квадрата было перед условием прямоугольника. Кроме того, как и в случае с выражениями catch, компилятор поможет вам пометить явные недостижимые условия. До этого вы не могли определить порядок выполнения, так что это не является нарушением существующего поведения.
• Условие по умолчанию (default) всегда вычисляется последним: несмотря на то, что после него идет условие null, условие default будет проверено после него. Это сделано для совместимости с существующей семантикой. Однако, как правило, вы помещаете условие default в конце.
• Условие null в конце достижимо, потому что шаблоны типов следуют примеру текущего is и не срабатывают для null. Это гарантирует, что null значения не будут случайно сопоставлены с первым шаблоном типа; вы должны явно указать, как им управлять (или оставить логику для условия default).
Переменные шаблона, объявленные ключевым словом case..., находятся в области видимости только в соответствующем разделе switch.
Кортежи
Обычно хочется вернуть несколько значений из метода. Все доступные варианты в существующих версиях C# являются менее оптимальными:
• Out параметры: использование является неэффективным (даже при использовании рассмотренных нововведений) и они не работают с асинхронными методами.
• System.Tuple <...>: выглядит многословным для использования и требует выделения кортежного объекта.
• Специальный вид переноса для каждого метода: слишком много кода для типа, единственной целью которого служит временная группировка нескольких значений.
• Анонимные типы, возвращаемые через тип возврата dynamic: потери в производительности и отсутствие проверки статического типа.
Для упрощения этой задачи в C# 7.0 были добавлены кортежи и литералы кортежей:
(string, string, string) LookupName(long id) // tuple return type
{
... // retrieve first, middle and last from data storage
return (first, middle, last); // tuple literal
}
Теперь метод эффективно возвращает три строки, объединенные как элементы кортежа.
Вызывающий код метода получит кортеж и может индивидуально иметь доступ к элементам:
var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}.");
Имена полей Item1 и т. д. являются именами по умолчанию для элементов кортежа и могут использоваться всегда. Но они не очень наглядны, поэтому вы можете добавить лучшие имена:
(string first, string middle, string last) LookupName(long id) // tuple elements have names
Теперь получатель этого кортежа имеет более описательные имена для дальнейшей работы:
var names = LookupName(id);
WriteLine($"found {names.first} {names.last}.");
Вы также можете указать имена элементов непосредственно в литералах кортежей:
return (first: first, middle: middle, last: last); // named tuple elements in a literal
Как правило, вы можете назначать типы кортежей друг для друга, независимо от их имен: при условии, что отдельные элементы будут присваиваемыми, типы кортежей могут свободно преобразовываться в другие типы кортежей.
Кортежи – это типы значений, а их элементы – общедоступные изменяемые поля. Они имеют значение равенства, а это значит, что два кортежа являются равными (и имеют одинаковый хэш-код), если все их элементы попарно равны (и имеют одинаковый хэш-код).
Это делает кортежи полезными для различных ситуаций, а не только для возвращения нескольких значений из метода. Например, если вам нужен словарь с составным ключом, используйте кортеж в качестве ключа, и все будет работать правильно. Если вам нужен список с несколькими значениями в каждой позиции, также используйте кортеж для корректной работы.
Кортежи полагаются на базовые структурные типы, которые называются ValueTuple <...>. Если вы выявите модель, что еще не включает эти типы, вы можете вместо этого выбрать их с помощью NuGet:
• Щелкните правой кнопкой мыши проект в обозревателе решений и выберите «Manage NuGet Packages…».
• Выберите вкладку «Browse» и выберите «nuget.org» в качестве «Package source».
• Найдите «System.ValueTuple» и установите его.
Распаковка кортежей
Еще один способ использования кортежа – это его распаковка. Объявление распаковки является синтаксисом для разделения кортежа (или другого значения) на его части и назначения этих частей по отдельности новым переменным:
(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");
В объявлении распаковки можно использовать ключевое слово var для отдельных переменных:
(var first, var middle, var last) = LookupName(id1); // var inside
Или даже поместить var перед скобками как аббревиатуру:
var (first, middle, last) = LookupName(id1); // var outside
Вы также можете распаковать в уже существующие переменные с помощью присвоения распаковки:
(first, middle, last) = LookupName(id2); // deconstructing assignment
Распаковка выполняется не только для кортежей. Любой тип может быть распакован, если у него есть метод распаковки (образец или расширение):
public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }
Out параметры соответствуют значениям, которые будут присвоены в результате распаковки.
(Почему используются out параметры, а не кортежи? Чтобы можно было иметь несколько перегрузок метода с разным количеством параметров).
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) { X = x; Y = y; }
public void Deconstruct(out int x, out int y) { x = X; y = Y; }
}
(var myX, var myY) = GetPoint(); // calls Deconstruct(out myX, out myY);
Это будет обычный шаблон для создания «симметричных» конструкторов и методов распаковки таким способом.
Так же, как и для out переменных, мы разрешаем «сбрасывать» в распаковке параметры, которые вам не нужны:
(var myX, _) = GetPoint(); // I only care about myX
Локальные функции
Иногда вспомогательная функция имеет смысл только внутри одного метода, в котором вызывается. Теперь вы можете объявить такие функции внутри других функций как локальную функцию:
public int Fibonacci(int x)
{
if (x < 0) throw new ArgumentException("Less negativity please!", nameof(x));
return Fib(x).current;
(int current, int previous) Fib(int i)
{
if (i == 0) return (1, 0);
var (p, pp) = Fib(i - 1);
return (p + pp, p);
}
}
Параметры и локальные переменные из области видимости доступны для локальной функции так же, как и для лямбда-выражений.
В качестве примера рассмотрим методы, реализованные как итераторы, что обычно нуждаются в неитераторном методе-оболочке для точной проверки аргументов в момент их вызова (так как сам итератор не запускается, пока не будет вызван MoveNext). Локальные функции идеально подходят для этого сценария:
public IEnumerable Filter(IEnumerable source, Func filter)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (filter == null) throw new ArgumentNullException(nameof(filter));
return Iterator();
IEnumerable Iterator()
{
foreach (var element in source)
{
if (filter(element)) { yield return element; }
}
}
}
Если бы Iterator был приватным методом рядом с Filter, то мог быть доступен для других членов в использовании напрямую (без проверки аргументов). Кроме того, необходимо было бы передавать все те же аргументы, что и Filter, вместо того, чтобы иметь их только в области видимости.
Улучшения литералов
В C# 7.0 появилась возможность добавлять «_» в качестве разделителя в числовые литералы:
var d = 123_456;
var x = 0xAB_CD_EF;
Вы можете поместить разделитель в любом месте между цифрами, чтобы улучшить читабельность. Они не влияют на значение.
Кроме того, C# 7.0 представляет бинарные литералы, так что вы можете указывать битовые шаблоны непосредственно вместо того, чтобы знать шестнадцатеричную систему наизусть.
var b = 0b1010_1011_1100_1101_1110_1111;
Локальные переменные и возвращаемые значения по ссылке
Теперь можно не только передать параметры в метод по ссылке в С# (с помощью ключевого слова ref), но и возвратить данные из метода по ссылке, а также сохранить в локальной переменной тоже по ссылке.
public ref int Find(int number, int[] numbers)
{
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] == number)
{
return ref numbers[i]; // return the storage location, not the value
}
}
throw new IndexOutOfRangeException($"{nameof(number)} not found");
}
int[] array = { 1, 15, -39, 0, 7, 14, -12 };
ref int place = ref Find(7, array); // aliases 7's place in the array
place = 9; // replaces 7 with 9 in the array
WriteLine(array[4]); // prints 9
Очень удобно передавать ссылки на определенные места в больших структурах данных. Например, в игре информация содержится в большом заранее выделенном массиве структур (во избежание пауз на сбор мусора). Теперь методы могут вернуть ссылку непосредственно на одну из таких структур, с помощью которой вызывающий код может читать и изменять эту структуру.
Существуют некоторые ограничения для обеспечения безопасности:
• Можно возвращать только ссылки, которые возвращать безопасно: ссылки, переданные в метод и ссылки на поля объектов.
• Локальные переменные инициализируются определенной ячейкой памяти и в будущем не меняются.
Обобщенные типы асинхронных возвратов
До сегодняшнего дня асинхронные методы могли возвращать только void, Task или Task. В C# 7.0 позволяется создавать типы, которые также могут быть возвращены асинхронным методом.
Например, можно создать структуру ValueTask, которая поможет избежать создания объекта Task в случае, когда результат асинхронной операции уже доступен в ожидаемое время. Для многих асинхронных сценариев, например, где используется буферизация, такой подход может значительно уменьшить число выделений памяти и таким образом значительно повысить производительность.
Конечно, можно придумать и другие ситуации, в которых task-подобные объекты будут полезны. Правильное создание таких типов не будет простой задачей, поэтому мы не ожидаем, что большое количество разработчиков будут создавать их. Однако мы полагаем, что они будут появляться в различных моделях и прикладных интерфейсах, и вызывающий код сможет просто использовать await, как сейчас для Task.
Больше членов в виде выражений
Методы и свойства в виде выражений используются в C# 6.0, но не все типы членов можно было так объявлять. В C# 7.0 к списку членов в виде выражений добавилась поддержка аксессоров, конструкторов и финализаторов:
class Person
{
private static ConcurrentDictionary names = new ConcurrentDictionary();
private int id = GetId();
public Person(string name) => names.TryAdd(id, name); // constructors
~Person() => names.TryRemove(id, out _); // finalizers
public string Name
{
get => names[id]; // getters
set => names[id] = value; // setters
}
}
Это пример функции, которая была предоставлена сообществом, а не командой компилятора Microsoft C#. Ура, открытый код!
Throw выражения
Выбросить исключение в середине выражения очень легко: достаточно вызвать метод, который это сделает! Но в C# 7 теперь можно использовать throw как часть выражения в определенном месте:
class Person
{
public string Name { get; }
public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));
public string GetFirstName()
{
var parts = Name.Split(" ");
return (parts.Length > 0) ? parts[0] : throw new InvalidOperationException("No name!");
}
public string GetLastName() => throw new NotImplementedException();
}
Источник
Реєстрацію на найбільше європейське ІТ-змагання – DEV Challenge XX відкрито!
Автор: Редакция ITVDN
Шукаєш нетривіальні виклики для своєї IT-кар'єри? Прагнеш продемонструвати свої навички та знання у справжньому бою?
👉Реєстрація за посиланням: https://cutt.ly/uwxW4OBd
Обирай свою номінацію:
Backend
Frontend
Testing
Product Design
UI Design
Крім призів отримуй:
🔸 Вихід на новий рівень складності завдань, з яким можеш впоратись, корисний стрес та апгрейд навичок.
🔸 Нетворкінг та обмін досвідом із експертами ІТ-сфери: 2500+ учасників та 40+ суддів за сезон. Гарна нагода знайти однодумців, партнерів по проєкту або менторів у потрібному напрямку.
🔸 Можливість заявити про себе та визнання як професійного ІТ-спеціаліста від провідних компаній-партнерів.
Традиційно, більшість завдань учасників будуть пов’язані з соціальними ІТ-рішеннями для допомоги Україні. Разом до перемоги!