День 1794. #ЗаметкиНаПолях
Препарируем Cookie в ASP.NET Core. Окончание
Начало
2. Cookie сессии
Сервис сессий в ASP.NET Core — это механизм управления пользовательскими данными между HTTP-запросами, который часто используется в таких сценариях, как обслуживание корзины покупок. Этот сервис отправляет браузеру защищённый файл cookie для отслеживания данных. Важно знать, что этот cookie и его назначение не связаны с cookie аутентификации.
Приложение может попросить сервис сессий запомнить временные данные текущего пользователя. Чтобы использовать сервис сессий, зарегистрируйте его в конвейере запросов:
Cookie сессии выглядит примерно так:
Этот cookie также защищён с помощью API защиты данных; однако для него нет возможности предоставить собственный класс защиты, как мы это сделали в предыдущем примере.
Один из вариантов посмотреть на содержимое — загрузить и изменить код промежуточного ПО сессий. Хотя, особой необходимости в этом нет. Cookie сессии хранит простой GUID - ключ, используемый для поиска объекта сессии для текущего запроса. Место хранения данных зависит от того, как вы настроили систему сессий. Система сессий в ASP.NET Core предоставляет различные варианты хранения, включая хранилище в памяти, распределённый кэш и внешних поставщиков хранилищ, что позволяет разработчикам выбирать наиболее подходящий метод для потребностей масштабируемости и производительности приложения.
3. Cookie защиты от подделки
Этот cookie предназначен для защиты от атак с подделкой межсайтовых запросов (CSRF). Он работает в системе на основе токенов, где токен хранится в файле cookie, а также встраивается в HTML-формы. При отправке формы токен из данных формы должен соответствовать токену в файле cookie для обеспечения безопасности.
DPAPI также защищает этот cookie. Реализацией этой защиты занимается класс DefaultAntiforgeryTokenSerializer, подробно изучить который можно здесь.
Вот пример такого cookie:
Не будем здесь углубляться в особенности внутренней структуры токена, это можно посмотреть в коде класса сериализатора по ссылке выше.
Источник: https://nestenius.se/2023/11/22/exploring-what-is-inside-the-asp-net-core-cookies/
Препарируем Cookie в ASP.NET Core. Окончание
Начало
2. Cookie сессии
Сервис сессий в ASP.NET Core — это механизм управления пользовательскими данными между HTTP-запросами, который часто используется в таких сценариях, как обслуживание корзины покупок. Этот сервис отправляет браузеру защищённый файл cookie для отслеживания данных. Важно знать, что этот cookie и его назначение не связаны с cookie аутентификации.
Приложение может попросить сервис сессий запомнить временные данные текущего пользователя. Чтобы использовать сервис сессий, зарегистрируйте его в конвейере запросов:
// регистрация сервиса
builder.Services.AddSession(options =>
{
//… параметры …
});
…
//использование сервиса
app.UseSession();
Cookie сессии выглядит примерно так:
Set-Cookie:
.AspNetCore.Session=CfDJ8MSO%2F2XEvalHlF%2Fgv69RLqD6mFWeTVC1MG3dYxNWftq625VyGyqF%2BeIq2
xaqgm1cd4McTp0ydSLcRYraIA4%2F%2Bn89FKFhz567AcC%2FeSnwabg4eRrlFAeWLFBq0K8zl2ISdMPcY0pj%
2BtAJQgC5NIte76QR4TlheM1ZhsD98WAdAvKM; path=/; samesite=lax; httponly
Этот cookie также защищён с помощью API защиты данных; однако для него нет возможности предоставить собственный класс защиты, как мы это сделали в предыдущем примере.
Один из вариантов посмотреть на содержимое — загрузить и изменить код промежуточного ПО сессий. Хотя, особой необходимости в этом нет. Cookie сессии хранит простой GUID - ключ, используемый для поиска объекта сессии для текущего запроса. Место хранения данных зависит от того, как вы настроили систему сессий. Система сессий в ASP.NET Core предоставляет различные варианты хранения, включая хранилище в памяти, распределённый кэш и внешних поставщиков хранилищ, что позволяет разработчикам выбирать наиболее подходящий метод для потребностей масштабируемости и производительности приложения.
3. Cookie защиты от подделки
Этот cookie предназначен для защиты от атак с подделкой межсайтовых запросов (CSRF). Он работает в системе на основе токенов, где токен хранится в файле cookie, а также встраивается в HTML-формы. При отправке формы токен из данных формы должен соответствовать токену в файле cookie для обеспечения безопасности.
DPAPI также защищает этот cookie. Реализацией этой защиты занимается класс DefaultAntiforgeryTokenSerializer, подробно изучить который можно здесь.
Вот пример такого cookie:
Set-Cookie: .AspNetCore.Antiforgery.YCD23e8AirM=CfDJ8MSO_2XEvalHlF_gv69RLqDarftldkzWvbxvac
LnZ4WtaTDcgSCPmxQdmK8OHbGfMIUvV0VhcFkx4Ys8jPppklGBUGWRTsXcwxhMBl7nqZA0s_xYN5jJq3J7
LrH8EikY82bOYmSoA_wEYCxkcrBEEAs; path=/; samesite=strict; httponly
Не будем здесь углубляться в особенности внутренней структуры токена, это можно посмотреть в коде класса сериализатора по ссылке выше.
Источник: https://nestenius.se/2023/11/22/exploring-what-is-inside-the-asp-net-core-cookies/
👍9
День 1795. #ЗаметкиНаПолях
Изоляция Данных в Модульном Монолите
Модульный монолит — это архитектурный подход, который становится очень популярным. Он пытается побороть недостатки монолитной и микросервисной архитектур. Одна из проблем монолитной архитектуры, — тесная связь между компонентами и зависимости между различными частями системы. Модульные монолиты решают эту проблему, чётко определяя границы модулей и шаблоны взаимодействия. Но один аспект, который нельзя упустить из виду, — это изоляция данных между модулями, что гарантирует, независимость и слабую связанность модулей.
Изоляция модулей облегчает внесение новых изменений в систему. Когда компоненты слабо связаны, возникает меньше непреднамеренных побочных эффектов.
Модульный монолит имеет строгие правила целостности данных:
- Каждый модуль может обращаться только к своим таблицам.
- Запрещено совместное использование таблиц или объектов между модулями.
- Объединения разрешены только между таблицами одного модуля.
Модули должны быть автономными. Каждый модуль обрабатывает только свои данные. Другие модули могут получить доступ к этим данным, используя общедоступный API модуля.
Уровни изоляции
1. Отдельные таблицы
Изначально у нас нет изоляции на уровне базы данных. Таблицы для всех модулей находятся внутри одной БД. Определить, какие таблицы к какому модулю принадлежат, непросто. Этот подход отлично работает до определённого размера приложения. Но чем больше у вас таблиц, тем сложнее становится изолировать их между модулями. Это можно улучшить, добавив логическую изоляцию между таблицами.
2. Раздельные схемы
Группировка связанных таблиц в БД — это способ обеспечить логическую изоляцию. Вы можете реализовать это, используя схемы. Каждый модуль имеет уникальную схему, содержащую таблицы модуля. Теперь легко отличить, какой модуль какие таблицы содержит. В приложении это легко реализовать, используя несколько контекстов базы данных в EF Core. Вы также можете ввести правила, предотвращающие запрос данных из других модулей и проверять это, например, с помощью тестов архитектуры. Это первый этап изоляции данных при создании модульного монолита.
3. Раздельные БД
Этот подход имеет больше ограничений, чем изоляция данных с помощью схем, однако это лучший вариант, если вам нужны строгие правила изоляции данных между модулями. Недостатком является большая сложность эксплуатации. Вам необходимо управлять инфраструктурой для нескольких БД. Однако это отличный шаг к извлечению модулей. Сначала вы перемещаете таблицы модуля, который хотите извлечь, в отдельную БД. Это также заставит вас решать любые проблемы связанности между вашими модулями. Если все таблицы модуля извлечены в отдельную БД, значит он готов (при необходимости) существовать независимо в виде микросервиса.
4. Различные хранилища
Не обязательно использовать один и тот же тип БД для всех модулей. Реляционные БД решают широкий спектр проблем. Но иногда БД документов или графов оказывается гораздо лучшим решением. Идея здесь аналогичная: вы выполняете изоляцию данных, используя отдельные БД, но используете другой тип БД для решения конкретных проблем. Например, реляционную базу данных для большинства модулей и БД документов для отдельного модуля, где она имеет преимущество перед реляционной. Это может быть выгодным компромиссом для вашего варианта использования. Но требует тщательного планирования.
Итого
Как минимум, стоит попробовать использовать логическую изоляцию с использованием схем. Её легко реализовать, и она помогает лучше понять границы модулей и держать связанность модулей под контролем.
Источник: https://www.milanjovanovic.tech/blog/modular-monolith-data-isolation
Изоляция Данных в Модульном Монолите
Модульный монолит — это архитектурный подход, который становится очень популярным. Он пытается побороть недостатки монолитной и микросервисной архитектур. Одна из проблем монолитной архитектуры, — тесная связь между компонентами и зависимости между различными частями системы. Модульные монолиты решают эту проблему, чётко определяя границы модулей и шаблоны взаимодействия. Но один аспект, который нельзя упустить из виду, — это изоляция данных между модулями, что гарантирует, независимость и слабую связанность модулей.
Изоляция модулей облегчает внесение новых изменений в систему. Когда компоненты слабо связаны, возникает меньше непреднамеренных побочных эффектов.
Модульный монолит имеет строгие правила целостности данных:
- Каждый модуль может обращаться только к своим таблицам.
- Запрещено совместное использование таблиц или объектов между модулями.
- Объединения разрешены только между таблицами одного модуля.
Модули должны быть автономными. Каждый модуль обрабатывает только свои данные. Другие модули могут получить доступ к этим данным, используя общедоступный API модуля.
Уровни изоляции
1. Отдельные таблицы
Изначально у нас нет изоляции на уровне базы данных. Таблицы для всех модулей находятся внутри одной БД. Определить, какие таблицы к какому модулю принадлежат, непросто. Этот подход отлично работает до определённого размера приложения. Но чем больше у вас таблиц, тем сложнее становится изолировать их между модулями. Это можно улучшить, добавив логическую изоляцию между таблицами.
2. Раздельные схемы
Группировка связанных таблиц в БД — это способ обеспечить логическую изоляцию. Вы можете реализовать это, используя схемы. Каждый модуль имеет уникальную схему, содержащую таблицы модуля. Теперь легко отличить, какой модуль какие таблицы содержит. В приложении это легко реализовать, используя несколько контекстов базы данных в EF Core. Вы также можете ввести правила, предотвращающие запрос данных из других модулей и проверять это, например, с помощью тестов архитектуры. Это первый этап изоляции данных при создании модульного монолита.
3. Раздельные БД
Этот подход имеет больше ограничений, чем изоляция данных с помощью схем, однако это лучший вариант, если вам нужны строгие правила изоляции данных между модулями. Недостатком является большая сложность эксплуатации. Вам необходимо управлять инфраструктурой для нескольких БД. Однако это отличный шаг к извлечению модулей. Сначала вы перемещаете таблицы модуля, который хотите извлечь, в отдельную БД. Это также заставит вас решать любые проблемы связанности между вашими модулями. Если все таблицы модуля извлечены в отдельную БД, значит он готов (при необходимости) существовать независимо в виде микросервиса.
4. Различные хранилища
Не обязательно использовать один и тот же тип БД для всех модулей. Реляционные БД решают широкий спектр проблем. Но иногда БД документов или графов оказывается гораздо лучшим решением. Идея здесь аналогичная: вы выполняете изоляцию данных, используя отдельные БД, но используете другой тип БД для решения конкретных проблем. Например, реляционную базу данных для большинства модулей и БД документов для отдельного модуля, где она имеет преимущество перед реляционной. Это может быть выгодным компромиссом для вашего варианта использования. Но требует тщательного планирования.
Итого
Как минимум, стоит попробовать использовать логическую изоляцию с использованием схем. Её легко реализовать, и она помогает лучше понять границы модулей и держать связанность модулей под контролем.
Источник: https://www.milanjovanovic.tech/blog/modular-monolith-data-isolation
👍12
День 1796.
В последний день года предлагаю вам вспомнить его. Поиграем в бинго? Присылайте свои ответы, от чего страдали (или чем наслаждались, кто вас знает 😉) в уходящем году.
PS: В Windows
В последний день года предлагаю вам вспомнить его. Поиграем в бинго? Присылайте свои ответы, от чего страдали (или чем наслаждались, кто вас знает 😉) в уходящем году.
PS: В Windows
Win+Shift+S
для снимка экрана, потом в уведомлениях Snip & Sketch для рисования на нём. Вычёркиваем то, что было.👍10
День 1797.
Дорогие подписчики, с новым годом!
Желаю вам кода без багов, зелёных тестов и вообще поменьше попадать в ситуации, описанные во вчерашнем «Бинго».
А ещё у меня для вас парочка подарков. Вот такие кружки разыграю среди подписчиков через несколько дней. Всё что нужно сделать, просто оставить комментарий «хочу кружку».
Одно замечание, победители, будьте готовы предоставить ФИО и адрес. И извините, доставка только по РФ.
Дорогие подписчики, с новым годом!
Желаю вам кода без багов, зелёных тестов и вообще поменьше попадать в ситуации, описанные во вчерашнем «Бинго».
А ещё у меня для вас парочка подарков. Вот такие кружки разыграю среди подписчиков через несколько дней. Всё что нужно сделать, просто оставить комментарий «хочу кружку».
Одно замечание, победители, будьте готовы предоставить ФИО и адрес. И извините, доставка только по РФ.
👍40
День 1798.
Зашёл на подкаст Radio DotNet и подвёл с ними итоги года. Довольно интересно поговорили, не знаю, будет ли интересно слушать, поэтому обязательно послушайте и расскажите, как вам 😊
Отмечу, что со звуком у меня фиговенько, извините, писал на встроенный микрофон.
Слушать тут.
Посмотреть можно тут.
Зашёл на подкаст Radio DotNet и подвёл с ними итоги года. Довольно интересно поговорили, не знаю, будет ли интересно слушать, поэтому обязательно послушайте и расскажите, как вам 😊
Отмечу, что со звуком у меня фиговенько, извините, писал на встроенный микрофон.
Слушать тут.
Посмотреть можно тут.
85 выпуск
Новогодние посиделки 2023 — Подкаст «RadioDotNet»
Подкаст RadioDotNet выпуск №85 от 31 декабря 2023 года Представляем вашему вниманию традиционный праздничный выпуск. В этот раз не будет никаких новостей. Вместе с нашими слушателями мы обсудим всякое. Всех с Новым Годом! Сайт подкаста: radio.dotn
👍17
День 1799. #TipsAndTricks
10 Крутых Трюков в C#. Начало
Предлагаю вам подборку крутых и малоизвестных трюков, которые помогут вам улучшить ваш код и повысить его эффективность. Начнём с простого.
1. Использование атрибутов информации о вызывающем объекте
Эти атрибуты позволяют получить информацию об объекте, вызывающем метод, что может быть полезно для отладки и логирования:
В примере выше метод Log() использует атрибуты информации о вызывающем объекте для автоматического включения имени члена вызывающего объекта, пути к файлу и номера строки в сообщение лога. Это упрощает отслеживание источников в логах без необходимости вручную включать эту информацию.
Пример вывода при вызове из Program.cs:
2. Эффективные по памяти коллекции с помощью Span<T>
Span<T> — позволяет работать с непрерывными блоками памяти без создания новых массивов или ненужного выделения памяти в куче. Его можно использовать для создания «срезов» существующих массивов или других сегментов памяти без копирования данных.
Как это работает и почему это полезно:
В приведенном выше примере метод ProcessArray обрабатывает большой массив байтов, разделяя его на более мелкие части. Вместо создания новых массивов для каждого фрагмента, что привело бы к увеличению использования памяти и накладным расходам на сбор мусора, метод использует Span<byte> для создания «представления» исходного массива для каждой части. Метод расширения AsSpan используется для создания Span<byte> из исходного массива, начиная с текущего индекса (i) и имеющего желаемую длину (len). Полученный Span<byte> можно использовать как массив, но это не приводит к дополнительному выделению памяти. Метод ProcessChunk демонстрирует, как можно работать со Span<byte> для манипуляций с данными, на которые он указывает. В этом случае значение каждого байта удваивается.
Использование Span<T> может помочь написать более эффективно использующий память и производительный код за счёт уменьшения количества аллокаций в куче и ненужного копирования данных, особенно при работе с большими наборами данных или в сценариях, критичных к производительности.
См. подробнее «Используем Span Для Производительности»
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Начало
Предлагаю вам подборку крутых и малоизвестных трюков, которые помогут вам улучшить ваш код и повысить его эффективность. Начнём с простого.
1. Использование атрибутов информации о вызывающем объекте
Эти атрибуты позволяют получить информацию об объекте, вызывающем метод, что может быть полезно для отладки и логирования:
public static void Log(
string message,
[CallerMemberName] string member = "",
[CallerFilePath] string path = "",
[CallerLineNumber] int line = 0)
{
Console.WriteLine(
$"[{member} ({line}) в {path}]: {message}");
}
// вызов
Log("Что-то пошло не так.");
В примере выше метод Log() использует атрибуты информации о вызывающем объекте для автоматического включения имени члена вызывающего объекта, пути к файлу и номера строки в сообщение лога. Это упрощает отслеживание источников в логах без необходимости вручную включать эту информацию.
Пример вывода при вызове из Program.cs:
[<Main>$ (7) в D:\source\Program.cs]: Что-то пошло не так.
2. Эффективные по памяти коллекции с помощью Span<T>
Span<T> — позволяет работать с непрерывными блоками памяти без создания новых массивов или ненужного выделения памяти в куче. Его можно использовать для создания «срезов» существующих массивов или других сегментов памяти без копирования данных.
public static void ProcessArray (byte[] arr)
{
const int size = 1024;
for (int i=0; i<arr.Length; i+=size)
{
int len = Math.Min(size, arr.Length - i);
var chunk = arr.AsSpan(i, len);
ProcessChunk(chunk);
}
}
public static void ProcessChunk(Span<byte> chunk)
{
// Обработка части без создания нового массива
for(int i=0; i<chunk.Length; i++)
chunk[i] *= 2;
}
Как это работает и почему это полезно:
В приведенном выше примере метод ProcessArray обрабатывает большой массив байтов, разделяя его на более мелкие части. Вместо создания новых массивов для каждого фрагмента, что привело бы к увеличению использования памяти и накладным расходам на сбор мусора, метод использует Span<byte> для создания «представления» исходного массива для каждой части. Метод расширения AsSpan используется для создания Span<byte> из исходного массива, начиная с текущего индекса (i) и имеющего желаемую длину (len). Полученный Span<byte> можно использовать как массив, но это не приводит к дополнительному выделению памяти. Метод ProcessChunk демонстрирует, как можно работать со Span<byte> для манипуляций с данными, на которые он указывает. В этом случае значение каждого байта удваивается.
Использование Span<T> может помочь написать более эффективно использующий память и производительный код за счёт уменьшения количества аллокаций в куче и ненужного копирования данных, особенно при работе с большими наборами данных или в сценариях, критичных к производительности.
См. подробнее «Используем Span Для Производительности»
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍33
День 1800. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
Начало
3. Использование оператора отказа
Оператор отказа (discard) используется для игнорирования значений, которые не нужны в определённом контексте, что делает код более кратким и простым для понимания.
В примере выше отказ (
Оператор отказа можно использовать в out-параметрах или в сопоставлении по шаблону, когда вам не нужно само значение:
4. Условная слабая таблица для привязки метаданных
Условная слабая таблица (Conditional Weak Table) позволяет связывать метаданные с объектами без изменения их исходной структуры. Она использует слабые ссылки, поэтому не мешает сборщику мусора утилизировать объекты, когда они больше не используются.
Как это работает и почему это полезно:
В примере выше запись Person не имеет метаданных. Статический класс PersonMetadata использует ConditionalWeakTable, чтобы связать метаданные с экземплярами Person без изменения исходного типа. Этот подход полезен, когда вы хотите хранить дополнительную информацию для объектов, не изменяя их структуру и не создавая строгие ссылки, которые могут помешать сборке мусора.
Методы Set и Get в классе PersonMetadata позволяют хранить и извлекать метаданные для экземпляров Person. Метаданные хранятся в словаре, который затем связывается с объектом с помощью ConditionalWeakTable. Таблица привязана слабой ссылкой, поэтому, когда объект больше не используется и подлежит сборке мусора, связанные метаданные также будут собраны.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
Начало
3. Использование оператора отказа
Оператор отказа (discard) используется для игнорирования значений, которые не нужны в определённом контексте, что делает код более кратким и простым для понимания.
(int min, _) = GetMinMax(numbers);
Console.WriteLine($"Minimum: {min}");
В примере выше отказ (
_
) используется, чтобы игнорировать значение max, возвращаемое методом GetMinMax(). Это ясно обозначает, что здесь нам не нужно максимальное значение.Оператор отказа можно использовать в out-параметрах или в сопоставлении по шаблону, когда вам не нужно само значение:
if(int.TryParse("123", out _))
Console.WriteLine("Это целое число.");
if (shape is Circle _)
Console.WriteLine("Фигура - круг");
4. Условная слабая таблица для привязки метаданных
Условная слабая таблица (Conditional Weak Table) позволяет связывать метаданные с объектами без изменения их исходной структуры. Она использует слабые ссылки, поэтому не мешает сборщику мусора утилизировать объекты, когда они больше не используются.
public record Person(string Name);
public static class PersonMetadata
{
private static readonly
ConditionalWeakTable<Person, Dictionary<string, object>>
Metadata = [];
public static void Set(
Person person,
string key,
object value)
{
var metadata = Metadata.GetOrCreateValue(person);
metadata[key] = value;
}
public static object? Get(Person person, string key)
{
if (
Metadata.TryGetValue(person, out var data)
&& data.TryGetValue(key, out var value))
{
return value;
}
return null;
}
}
// использование
var person = new Person("Jon Smith");
PersonMetadata.Set(person, "age", 42);
Console.WriteLine(PersonMetadata.Get(person, "age"));
Как это работает и почему это полезно:
В примере выше запись Person не имеет метаданных. Статический класс PersonMetadata использует ConditionalWeakTable, чтобы связать метаданные с экземплярами Person без изменения исходного типа. Этот подход полезен, когда вы хотите хранить дополнительную информацию для объектов, не изменяя их структуру и не создавая строгие ссылки, которые могут помешать сборке мусора.
Методы Set и Get в классе PersonMetadata позволяют хранить и извлекать метаданные для экземпляров Person. Метаданные хранятся в словаре, который затем связывается с объектом с помощью ConditionalWeakTable. Таблица привязана слабой ссылкой, поэтому, когда объект больше не используется и подлежит сборке мусора, связанные метаданные также будут собраны.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍36
День 1801. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5. Конвейеры для потоковой обработки
Pipelines — библиотека потоковой обработки в .NET для эффективной обработки больших потоков данных с низкими задержками. Пространство имён System.IO.Pipelines предоставляет абстракции для чтения и записи данных в потоковом режиме, минимизируя при этом выделение памяти и копирование.
Вывод:
Как это работает и почему это полезно:
В примере выше метод ProcessStream создаёт новый экземпляр Pipe и запускает две задачи: для записи данных в канал (FillPipe) и для чтения данных из канала (ReadPipe). Task.WhenAll используется для ожидания завершения обеих задач. FillPipe считывает данные из входного потока и записывает их в PipeWriter. Он делает это в цикле, получая память от PipeWriter и считывая данные из потока в память. Метод PipeWriter.FlushAsync вызывается, чтобы сигнализировать о том, что данные доступны для чтения, и цикл продолжается до тех пор, пока поток не будет исчерпан или канал не будет закрыт. Метод ReadPipeAsync ожидает PipeReader.ReadAsync, который возвращает ReadResult, содержащий буфер ReadOnlySequence<byte>. Буфер обрабатывается частями, а метод PipeReader.AdvanceTo вызывается, чтобы сигнализировать о том, что данные были использованы. Цикл продолжается до тех пор, пока конвейер не будет завершён.
Использование конвейеров для потоковой обработки может обеспечить значительный выигрыш в производительности, особенно в сценариях, где требуется обработка с малой задержкой и минимальное выделение памяти. Абстракции и дизайн библиотеки упрощают обработку сложных сценариев потоковой передачи, таких как сетевое взаимодействие или файловый ввод-вывод, с эффективным управлением ресурсами и оптимальной производительностью.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5. Конвейеры для потоковой обработки
Pipelines — библиотека потоковой обработки в .NET для эффективной обработки больших потоков данных с низкими задержками. Пространство имён System.IO.Pipelines предоставляет абстракции для чтения и записи данных в потоковом режиме, минимизируя при этом выделение памяти и копирование.
async Task ProcessStream(
Stream stream)
{
var pipe = new Pipe();
var write = FillPipe(stream, pipe.Writer);
var read = ReadPipe(pipe.Reader);
await Task.WhenAll(write, read);
}
async Task FillPipe(
Stream stream,
PipeWriter writer
)
{
const int minSize = 512;
while (true)
{
var memory = writer.GetMemory(minSize);
int bytes = await stream.ReadAsync(memory);
if (bytes == 0)
break;
writer.Advance(bytes);
var result = await writer.FlushAsync();
if (result.IsCompleted)
break;
}
writer.Complete();
}
async Task ReadPipe(
PipeReader reader)
{
while (true)
{
var result = await reader.ReadAsync();
var buffer = result.Buffer;
foreach (var seg in buffer)
{
// Обработка данных частями
Console.WriteLine(
$"Прочитано {seg.Length} байт");
}
reader.AdvanceTo(buffer.End);
if (result.IsCompleted)
break;
}
reader.Complete();
}
// использование
using var stream =
new MemoryStream(
Encoding.UTF8.GetBytes("Hello, Pipelines!"));
await ProcessStream(stream);
Вывод:
Прочитано 17 байт
Как это работает и почему это полезно:
В примере выше метод ProcessStream создаёт новый экземпляр Pipe и запускает две задачи: для записи данных в канал (FillPipe) и для чтения данных из канала (ReadPipe). Task.WhenAll используется для ожидания завершения обеих задач. FillPipe считывает данные из входного потока и записывает их в PipeWriter. Он делает это в цикле, получая память от PipeWriter и считывая данные из потока в память. Метод PipeWriter.FlushAsync вызывается, чтобы сигнализировать о том, что данные доступны для чтения, и цикл продолжается до тех пор, пока поток не будет исчерпан или канал не будет закрыт. Метод ReadPipeAsync ожидает PipeReader.ReadAsync, который возвращает ReadResult, содержащий буфер ReadOnlySequence<byte>. Буфер обрабатывается частями, а метод PipeReader.AdvanceTo вызывается, чтобы сигнализировать о том, что данные были использованы. Цикл продолжается до тех пор, пока конвейер не будет завершён.
Использование конвейеров для потоковой обработки может обеспечить значительный выигрыш в производительности, особенно в сценариях, где требуется обработка с малой задержкой и минимальное выделение памяти. Абстракции и дизайн библиотеки упрощают обработку сложных сценариев потоковой передачи, таких как сетевое взаимодействие или файловый ввод-вывод, с эффективным управлением ресурсами и оптимальной производительностью.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍25👎1
День 1802. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6. Рефлексия и деревья выражений
Деревья выражений могут быть полезны для сложных сценариев рефлексии, таких как создание динамических методов или оптимизация путей кода, критичных к производительности.
Вывод:
Как это работает и почему это полезно:
В примере выше метод GeneratePropertyGetter<T> демонстрирует, как использовать деревья выражений для создания метода получения свойств для заданного класса и имени свойства. Метод принимает параметр типа T и строку, представляющую имя свойства, затем создаёт дерево выражений, которое предоставляет доступ к свойству в экземпляре T и возвращает его значение.
Дерево выражений создается с использованием методов класса Expression, таких как Expression.Parameter, Expression.Property и Expression.Lambda. После завершения создания дерева выражений вызывается метод Compile для создания делегата Func<T, object>, который можно использовать для вызова метода получения свойств во время выполнения. Метод GeneratePropertyGetter используется для создания методов получения свойств для свойства Name и Age записи Person. Эти методы получения свойств затем используются для получения значений свойств из экземпляра Person.
Использование деревьев выражений в связке с рефлексией может дать несколько преимуществ, таких как повышение производительности, гибкость и возможность генерировать и компилировать код во время выполнения. Однако имейте в виду, что деревья выражений могут быть более сложными и трудными для отладки, чем традиционные методы рефлексии, поэтому используйте их разумно и только при необходимости.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6. Рефлексия и деревья выражений
Деревья выражений могут быть полезны для сложных сценариев рефлексии, таких как создание динамических методов или оптимизация путей кода, критичных к производительности.
Func<T, object> GeneratePropertyGetter<T>(
string property
)
{
var param =
Expression.Parameter(typeof(T), "obj");
var prop =
Expression.Property(param, property);
var conv =
Expression.Convert(prop, typeof(object));
var lambda =
Expression.Lambda<Func<T, object>>(conv, param);
return lambda.Compile();
}
// использование
var person = new Person("John Doe", 30);
var getName = GeneratePropertyGetter<Person>("Name");
var getAge = GeneratePropertyGetter<Person>("Age");
Console.WriteLine(
$"Name: {getName(person)}, Age: {getAge(person)}");
record Person(string Name, int Age);
Вывод:
Name: John Doe, Age: 30
Как это работает и почему это полезно:
В примере выше метод GeneratePropertyGetter<T> демонстрирует, как использовать деревья выражений для создания метода получения свойств для заданного класса и имени свойства. Метод принимает параметр типа T и строку, представляющую имя свойства, затем создаёт дерево выражений, которое предоставляет доступ к свойству в экземпляре T и возвращает его значение.
Дерево выражений создается с использованием методов класса Expression, таких как Expression.Parameter, Expression.Property и Expression.Lambda. После завершения создания дерева выражений вызывается метод Compile для создания делегата Func<T, object>, который можно использовать для вызова метода получения свойств во время выполнения. Метод GeneratePropertyGetter используется для создания методов получения свойств для свойства Name и Age записи Person. Эти методы получения свойств затем используются для получения значений свойств из экземпляра Person.
Использование деревьев выражений в связке с рефлексией может дать несколько преимуществ, таких как повышение производительности, гибкость и возможность генерировать и компилировать код во время выполнения. Однако имейте в виду, что деревья выражений могут быть более сложными и трудными для отладки, чем традиционные методы рефлексии, поэтому используйте их разумно и только при необходимости.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍11
День 1803. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7. Упрощение многопоточности с помощью каналов
Каналы — это примитив синхронизации, представленный в .NET Core 3.0, который упрощает многопоточность, предоставляя потокам возможность взаимодействовать и обмениваться данными в потокобезопасном режиме. Их можно использовать для реализации шаблона производитель-потребитель, позволяющего разделить производство и потребление данных.
Как это работает и почему это полезно:
В примере выше метод ProcessData демонстрирует простой сценарий производитель-потребитель с использованием канала. Переменная канала инициализируется как неограниченный канал, то есть может хранить неограниченное количество элементов.
Задача производителя генерирует данные (целые числа от 1 до 10) и записывает их в канал с помощью метода WriteAsync. Задача потребителя считывает данные из канала с помощью метода ReadAllAsync и обрабатывает их. В этом случае она просто выводит на консоль полученные данные.
Задачи производителя и потребителя выполняются одновременно, что позволяет потребителю обрабатывать данные, как только они становятся доступными. Класс Channel гарантирует, что обмен данными является потокобезопасным, что упрощает написание многопоточного кода, не беспокоясь о блокировках или других механизмах синхронизации.
Каналы можно использовать в различных сценариях, таких как конвейеры обработки данных, распараллеливание рабочих нагрузок или реализация связи между компонентами в многопоточном приложении. Они предоставляют простой в использовании и эффективный способ управления параллелизмом и обменом данными между потоками.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7. Упрощение многопоточности с помощью каналов
Каналы — это примитив синхронизации, представленный в .NET Core 3.0, который упрощает многопоточность, предоставляя потокам возможность взаимодействовать и обмениваться данными в потокобезопасном режиме. Их можно использовать для реализации шаблона производитель-потребитель, позволяющего разделить производство и потребление данных.
async Task ProcessData()
{
var channel = Channel.CreateUnbounded<int>();
var producer = Task.Run(async () =>
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($"Произведено: {i}");
await channel.Writer.WriteAsync(i);
await Task.Delay(1000);
}
channel.Writer.Complete();
});
var consumer = Task.Run(async () =>
{
await foreach (
var i in channel.Reader.ReadAllAsync())
{
Console.WriteLine($"Обработано: {i}");
}
});
await Task.WhenAll(producer, consumer);
}
Как это работает и почему это полезно:
В примере выше метод ProcessData демонстрирует простой сценарий производитель-потребитель с использованием канала. Переменная канала инициализируется как неограниченный канал, то есть может хранить неограниченное количество элементов.
Задача производителя генерирует данные (целые числа от 1 до 10) и записывает их в канал с помощью метода WriteAsync. Задача потребителя считывает данные из канала с помощью метода ReadAllAsync и обрабатывает их. В этом случае она просто выводит на консоль полученные данные.
Задачи производителя и потребителя выполняются одновременно, что позволяет потребителю обрабатывать данные, как только они становятся доступными. Класс Channel гарантирует, что обмен данными является потокобезопасным, что упрощает написание многопоточного кода, не беспокоясь о блокировках или других механизмах синхронизации.
Каналы можно использовать в различных сценариях, таких как конвейеры обработки данных, распараллеливание рабочих нагрузок или реализация связи между компонентами в многопоточном приложении. Они предоставляют простой в использовании и эффективный способ управления параллелизмом и обменом данными между потоками.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍31
День 1804. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8. Динамическая компиляция кода с Roslyn
Динамическая компиляция кода с помощью Roslyn позволяет компилировать и выполнять код C# во время выполнения. Это может быть полезно для сценариев, плагинов или ситуаций, когда код необходимо генерировать или изменять «на лету».
Вывод:
Как это работает и почему это полезно:
В примере выше метод ExecuteDynamicCodeAsync демонстрирует, как скомпилировать и выполнить фрагмент кода C# во время выполнения с помощью компилятора Roslyn. Метод Roslyn CSharpSyntaxTree.ParseText используется для анализа исходного кода в синтаксическое дерево, которое затем добавляется в новый объект CSharpCompilation. К объекту компиляции также добавляются необходимые ссылки на сборки: библиотеку CoreLib, System.Runtime и System.Console для работы класса Console. Заметьте, что в последних версиях .NET они находятся в разных файлах.
Метод Emit компилирует код в динамически подключаемую библиотеку (DLL) и записывает выходные данные в MemoryStream. Если компиляция прошла успешно, полученная сборка загружается в текущий домен приложения с помощью метода Assembly.Load. Затем класс Runner и его метод Run получаются через рефлексию, и этот метод вызывается, выполняя динамический код.
Этот метод позволяет создавать гибкие и расширяемые приложения, которые могут динамически компилировать и выполнять код C# во время выполнения. Однако будьте осторожны с последствиями для безопасности, поскольку выполнение произвольного кода может создать угрозу безопасности, если с ним не обращаться должным образом.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8. Динамическая компиляция кода с Roslyn
Динамическая компиляция кода с помощью Roslyn позволяет компилировать и выполнять код C# во время выполнения. Это может быть полезно для сценариев, плагинов или ситуаций, когда код необходимо генерировать или изменять «на лету».
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
async Task ExecuteDynamicCodeAsync(
string code)
{
string sourceCode = $@"
using System;
namespace DynamicCode;
public class Runner
{{
public static void Run()
{{
{code}
}}
}}
";
var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
var references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Private.CoreLib.dll")),
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Console.dll")),
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Runtime.dll"))
};
var compilation = CSharpCompilation.Create("DynamicCode")
.WithOptions(new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary))
.AddReferences(references)
.AddSyntaxTrees(syntaxTree);
using var ms = new MemoryStream();
var result = compilation.Emit(ms);
if (!result.Success)
{
Console.WriteLine("Компиляция не удалась");
return;
}
ms.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(ms.ToArray());
var type = assembly.GetType("DynamicCode.Runner");
var method = type.GetMethod("Run",
BindingFlags.Static | BindingFlags.Public);
method.Invoke(null, null);
}
// использование
await ExecuteDynamicCodeAsync(
"Console.WriteLine(\"Привет, динамический код!\");"
);
Вывод:
Привет, динамический код!
Как это работает и почему это полезно:
В примере выше метод ExecuteDynamicCodeAsync демонстрирует, как скомпилировать и выполнить фрагмент кода C# во время выполнения с помощью компилятора Roslyn. Метод Roslyn CSharpSyntaxTree.ParseText используется для анализа исходного кода в синтаксическое дерево, которое затем добавляется в новый объект CSharpCompilation. К объекту компиляции также добавляются необходимые ссылки на сборки: библиотеку CoreLib, System.Runtime и System.Console для работы класса Console. Заметьте, что в последних версиях .NET они находятся в разных файлах.
Метод Emit компилирует код в динамически подключаемую библиотеку (DLL) и записывает выходные данные в MemoryStream. Если компиляция прошла успешно, полученная сборка загружается в текущий домен приложения с помощью метода Assembly.Load. Затем класс Runner и его метод Run получаются через рефлексию, и этот метод вызывается, выполняя динамический код.
Этот метод позволяет создавать гибкие и расширяемые приложения, которые могут динамически компилировать и выполнять код C# во время выполнения. Однако будьте осторожны с последствиями для безопасности, поскольку выполнение произвольного кода может создать угрозу безопасности, если с ним не обращаться должным образом.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍15
День 1805.
Итак, результаты розыгрыша подарочных кружек. Как обычно, определил его величество рандом. Кружек две и победителей двое.
@juju_true и @NataliEgorsheva, поздравляю! 🥳
Пожалуйста, свяжитесь со мной в личке.
Итак, результаты розыгрыша подарочных кружек. Как обычно, определил его величество рандом. Кружек две и победителей двое.
@juju_true и @NataliEgorsheva, поздравляю! 🥳
Пожалуйста, свяжитесь со мной в личке.
👍18👎1
День 1806. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8
9. Преобразование анонимных типов в dynamic
Преобразование анонимных типов в объекты dynamic может обеспечить большую гибкость при манипулировании данными. Анонимные типы доступны только для чтения и строго типизированы, что может ограничивать возможности изменения или расширения данных. Преобразуя анонимный тип в динамический
ExpandoObject вы получаете возможность добавлять, удалять или изменять свойства во время выполнения.
Как это работает и почему это полезно:
В примере выше метод ToDynamic принимает анонимный объект в качестве входных данных и преобразует его в динамический ExpandoObject. Это делается путем перебора свойств анонимного объекта с помощью TypeDescriptor.GetProperties и добавления их в ExpandoObject с помощью интерфейса IDictionary<string, object>.
Далее демонстрируется, как использовать метод ToDynamic для преобразования анонимного объекта в динамический объект. Переменная anon содержит анонимный объект со свойствами Name и Age. После преобразования его в динамический объект с помощью ToDynamic вы можете напрямую получать доступ к его свойствам и изменять их, а также добавлять новые свойства, такие как City.
Преобразование анонимных типов в динамические объекты может быть полезно, когда вам нужна большая гибкость со структурами данных, особенно при работе с динамически генерируемыми данными или когда схема неизвестна во время компиляции. Однако имейте в виду, что использование динамических объектов может привести к потере проверки типов во время компиляции и потенциальным проблемам производительности.
Окончание следует...
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8
9. Преобразование анонимных типов в dynamic
Преобразование анонимных типов в объекты dynamic может обеспечить большую гибкость при манипулировании данными. Анонимные типы доступны только для чтения и строго типизированы, что может ограничивать возможности изменения или расширения данных. Преобразуя анонимный тип в динамический
ExpandoObject вы получаете возможность добавлять, удалять или изменять свойства во время выполнения.
dynamic ToDynamic(object anon)
{
var dyn = new ExpandoObject()
as IDictionary<string, object>;
var props = TypeDescriptor.GetProperties(anon);
foreach (PropertyDescriptor p in props)
dyn.Add(p.Name, p.GetValue(anon));
return dyn;
}
// использование
var anon = new { Name = "John", Age = 30 };
dynamic dyn = ToDynamic(anon);
Console.WriteLine(
$"Name: {dyn.Name}, Age: {dyn.Age}");
dyn.Age = 35;
dyn.City = "New York";
Console.WriteLine(
@$"Name: {dyn.Name},
Age: {dyn.Age},
City:{dyn.City}");
Как это работает и почему это полезно:
В примере выше метод ToDynamic принимает анонимный объект в качестве входных данных и преобразует его в динамический ExpandoObject. Это делается путем перебора свойств анонимного объекта с помощью TypeDescriptor.GetProperties и добавления их в ExpandoObject с помощью интерфейса IDictionary<string, object>.
Далее демонстрируется, как использовать метод ToDynamic для преобразования анонимного объекта в динамический объект. Переменная anon содержит анонимный объект со свойствами Name и Age. После преобразования его в динамический объект с помощью ToDynamic вы можете напрямую получать доступ к его свойствам и изменять их, а также добавлять новые свойства, такие как City.
Преобразование анонимных типов в динамические объекты может быть полезно, когда вам нужна большая гибкость со структурами данных, особенно при работе с динамически генерируемыми данными или когда схема неизвестна во время компиляции. Однако имейте в виду, что использование динамических объектов может привести к потере проверки типов во время компиляции и потенциальным проблемам производительности.
Окончание следует...
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍7👎1
День 1807. #TipsAndTricks
10 Крутых Трюков в C#. Окончание
1-2
3-4
5
6
7
8
9
10. Простой пул объектов для повторно используемых ресурсов
Пул объектов — это шаблон проектирования, который помогает повторно использовать объекты, создание которых требует больших затрат, например соединения с базой данных или большие буферы памяти. Создав пул предварительно выделенных объектов и повторно используя их при необходимости, вы можете повысить производительность приложения и снизить накладные расходы, связанные с созданием и уничтожением объектов.
Как это работает и почему это полезно:
В примере выше класс ObjectPool<T> является обобщённой реализацией пула объектов. Он использует ConcurrentBag<T> для хранения объектов и делегат Func<T> для создания новых объектов при необходимости. Метод Get извлекает объект из пула, если он доступен, или создает новый, если пул пуст. Метод Return возвращает объект в пул, когда он больше не нужен. Класс Expensive представляет гипотетический ресурс, создание которого требует больших затрат.
В примере использования создается экземпляр ObjectPool<Expensive>, а экземпляр Expensive извлекается из пула с помощью метода Get. После манипуляции со свойствами объекта вызывается метод Return, который возвращает объект в пул для будущего повторного использования.
Использование пула объектов может повысить производительность приложения и сократить использование памяти за счет минимизации количества созданий и удалений объектов. Это особенно полезно в высокопроизводительных средах или средах с ограниченными ресурсами, где создание объектов и сборка мусора могут стать серьёзными узкими местами. Однако имейте в виду, что объединение объектов в пул усложняет код и может подходить не для всех сценариев.
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Окончание
1-2
3-4
5
6
7
8
9
10. Простой пул объектов для повторно используемых ресурсов
Пул объектов — это шаблон проектирования, который помогает повторно использовать объекты, создание которых требует больших затрат, например соединения с базой данных или большие буферы памяти. Создав пул предварительно выделенных объектов и повторно используя их при необходимости, вы можете повысить производительность приложения и снизить накладные расходы, связанные с созданием и уничтожением объектов.
class ObjectPool<T> where T : new()
{
private readonly ConcurrentBag<T> _objects;
private readonly Func<T> _generator;
public ObjectPool(Func<T>? generator = null)
{
_generator = generator ??
(() => new T());
_objects = [];
}
public T Get()
{
return _objects.TryTake(out T item)
? item
: _generator();
}
public void Return(T item)
{
_objects.Add(item);
}
}
class Expensive
{
public int Value { get; set; }
}
// использование
var pool = new ObjectPool<Expensive>();
var resource = pool.Get();
resource.Value = 42;
Console.WriteLine(
$"Значение ресурса: {resource.Value}");
pool.Return(resource);
Как это работает и почему это полезно:
В примере выше класс ObjectPool<T> является обобщённой реализацией пула объектов. Он использует ConcurrentBag<T> для хранения объектов и делегат Func<T> для создания новых объектов при необходимости. Метод Get извлекает объект из пула, если он доступен, или создает новый, если пул пуст. Метод Return возвращает объект в пул, когда он больше не нужен. Класс Expensive представляет гипотетический ресурс, создание которого требует больших затрат.
В примере использования создается экземпляр ObjectPool<Expensive>, а экземпляр Expensive извлекается из пула с помощью метода Get. После манипуляции со свойствами объекта вызывается метод Return, который возвращает объект в пул для будущего повторного использования.
Использование пула объектов может повысить производительность приложения и сократить использование памяти за счет минимизации количества созданий и удалений объектов. Это особенно полезно в высокопроизводительных средах или средах с ограниченными ресурсами, где создание объектов и сборка мусора могут стать серьёзными узкими местами. Однако имейте в виду, что объединение объектов в пул усложняет код и может подходить не для всех сценариев.
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍9👎1
День 1808. #ЧтоНовенького #CSharp13
Коллекции в Params
Всего месяц после выпуска .NET 8, а команда dotnet уже работает над следующей итерацией: .NET 9. Сегодня посмотрим на одно из предлагаемых нововведений: коллекции в параметрах с ключевым словом params.
Ключевое слово params в C# появилось давно. Оно позволяет передавать в метод переменное количество аргументов:
До сих пор оно было доступно только для массивов. Нововведение позволяет использовать его для коллекций любого типа:
И теперь эти методы могут быть вызваны любым способом:
Это позволяет добавить вариативности в использовании ваших методов клиентами и иметь более эффективный код внутри метода. Для полноты картины, в C# 12 вы уже могли сделать так:
Тем не менее, новая версия удобнее. Также обещают решить проблемы производительности, присущие нынешней версии params, но это не точно (с).
Подробнее про нововведение можно почитать в предложении на GitHub или попробовать его в действии на SharpLab.
Источник: https://steven-giesel.com/blogPost/5f4fef86-d251-4a47-b893-ca4c515ca314/the-first-possible-new-feature-of-c-13-params-collection
Коллекции в Params
Всего месяц после выпуска .NET 8, а команда dotnet уже работает над следующей итерацией: .NET 9. Сегодня посмотрим на одно из предлагаемых нововведений: коллекции в параметрах с ключевым словом params.
Ключевое слово params в C# появилось давно. Оно позволяет передавать в метод переменное количество аргументов:
void PrintNums(params int[] nums)
{
foreach (var n in nums)
Console.WriteLine(n);
}
До сих пор оно было доступно только для массивов. Нововведение позволяет использовать его для коллекций любого типа:
void PrintNumsSpan(
params ReadOnlySpan<int> nums)
{
foreach (var n in nums)
Console.WriteLine(n);
}
void PrintNumsList(
params List<int> nums)
{
nums.ForEach(Console.WriteLine);
}
И теперь эти методы могут быть вызваны любым способом:
PrintNumsSpan(1, 2, 3);
PrintNumsList([1, 2, 3]);
Это позволяет добавить вариативности в использовании ваших методов клиентами и иметь более эффективный код внутри метода. Для полноты картины, в C# 12 вы уже могли сделать так:
List<int> numbers = [1,2,3];
PrintNums([..numbers]);
Тем не менее, новая версия удобнее. Также обещают решить проблемы производительности, присущие нынешней версии params, но это не точно (с).
Подробнее про нововведение можно почитать в предложении на GitHub или попробовать его в действии на SharpLab.
Источник: https://steven-giesel.com/blogPost/5f4fef86-d251-4a47-b893-ca4c515ca314/the-first-possible-new-feature-of-c-13-params-collection
👍22
День 1809. #Оффтоп
Самая Сложная Часть Создания ПО — не Кодирование, а ТЗ. Начало
Глядя на новости о разработках в области искусственного интеллекта, многие боятся того, что мы, как разработчики ПО, вскоре можем остаться без работы, и нас заменит ИИ. Они полагают, что бизнес будет напрямую просить ИИ создать то, что, по их мнению, им нужно. Стоит ли этого бояться?
Программирование может оказаться непростой задачей, но разобраться в коде, в принципе, не так сложно. Как только вы освоите синтаксис, логику и методы, написание кода станет довольно простым процессом в большинстве случаев. Настоящие проблемы обычно связаны с тем, что должно делать ПО. Самое сложное в создании ПО — создание требований, а требования к ПО по-прежнему определяются людьми.
Это не баг, это фича… нет, погодите, это баг
Я как-то работал над созданием ПО, которое настраивало индивидуальные условия для продуктов на сайтах электронной торговли. Существовало размытое описание логики, которая зависела от типа товара и штата США, где находился заказчик в силу требований закона.
В какой-то момент мне показалось, что я нашел баг. Пользователь выбирает один тип продукта, что создаёт соответствующие условия, но далее в рабочем процессе он может изменить тип продукта. Это нарушало условия, под которыми стояла подпись клиента. Я спросил клиента, надо ли убрать возможность менять тип продукта или при смене переопределять условия? Ответ был уверенным: "Этого никогда не произойдёт".
Это был старший руководитель, не было повода ему не доверять. Несколько месяцев спустя, перед релизом ПО, тестировщик обнаружил дефект, и его поручил мне. Угадайте, что это было, кого в этом обвинили и кого попросили это исправить? Исправить ошибку было легко, а последствия были незначительными, но это лишь в этот раз. Проблемы больше, их сложнее исправить, и они обходятся дороже, чем дальше в процессе разработки вы продвигаетесь. Но источник проблем обычно один и тот же: требования были неясными, непоследовательными или неправильными.
ИИ сейчас: шахматы против беспилотных автомобилей
Концепция ИИ существует уже довольно давно. Он был применён в шахматах ещё в 80-х годах. К концу 90-х ИИ превзошёл возможности человека выигрывать в шахматы. Это тоже неудивительно, ведь параметры шахмат КОНЕЧНЫ. При каждом ходе существует конечное число возможных ходов. ИИ может рассчитывать последствия каждого хода, чтобы выбрать наиболее подходящий, приводящий в итоге к победе.
Другая сфера - беспилотные автомобили. Производители уже довольно давно их обещают. Они в основном используют механизмы, основанные на правилах, для принятия решений. Но здесь, в отличие от шахмат, правила поведения во всех возможных ситуациях чётко не определены. Водители в поездке принимают тысячи мелких решений. Правильность этих решений часто означает разницу между прибытием в пункт назначения и аварией.
В сфере технологий есть понятие для выражения доступности сервиса в виде количества девяток. Стандартом сейчас является 5 девяток, т.е. доступность 99,999% времени. Проблема в том, что достичь уровня доступности в 99% легко, но каждая следующая девятка даётся сложнее в геометрической прогрессии.
Причина, по которой так сложно достичь приемлемого уровня безопасности беспилотных авто в том, что вождение автомобиля предполагает значительно больше переменных, чем шахматы, и эти переменные НЕ КОНЕЧНЫ. Первые 95% или 99% могут быть предсказуемыми и легко поддающимися учёту. Но дальше существует очень много крайних случаев, которые могут иметь некоторые общие черты, но каждый из которых уникален: другие машины, управляемые людьми, перекрытия дорог, аварии, погодные явления. Невероятно сложно заставить модель ИИ учитывать и распознавать эти аномалии, и, что более важно, правильно реагировать, не попадая в аварию.
Окончание следует…
Источник: https://stackoverflow.blog/2023/12/29/the-hardest-part-of-building-software-is-not-coding-its-requirements/
Автор оригинала: Jared Toporek (консультант в разработке ПО, создатель приложения Keenforms)
Самая Сложная Часть Создания ПО — не Кодирование, а ТЗ. Начало
Глядя на новости о разработках в области искусственного интеллекта, многие боятся того, что мы, как разработчики ПО, вскоре можем остаться без работы, и нас заменит ИИ. Они полагают, что бизнес будет напрямую просить ИИ создать то, что, по их мнению, им нужно. Стоит ли этого бояться?
Программирование может оказаться непростой задачей, но разобраться в коде, в принципе, не так сложно. Как только вы освоите синтаксис, логику и методы, написание кода станет довольно простым процессом в большинстве случаев. Настоящие проблемы обычно связаны с тем, что должно делать ПО. Самое сложное в создании ПО — создание требований, а требования к ПО по-прежнему определяются людьми.
Это не баг, это фича… нет, погодите, это баг
Я как-то работал над созданием ПО, которое настраивало индивидуальные условия для продуктов на сайтах электронной торговли. Существовало размытое описание логики, которая зависела от типа товара и штата США, где находился заказчик в силу требований закона.
В какой-то момент мне показалось, что я нашел баг. Пользователь выбирает один тип продукта, что создаёт соответствующие условия, но далее в рабочем процессе он может изменить тип продукта. Это нарушало условия, под которыми стояла подпись клиента. Я спросил клиента, надо ли убрать возможность менять тип продукта или при смене переопределять условия? Ответ был уверенным: "Этого никогда не произойдёт".
Это был старший руководитель, не было повода ему не доверять. Несколько месяцев спустя, перед релизом ПО, тестировщик обнаружил дефект, и его поручил мне. Угадайте, что это было, кого в этом обвинили и кого попросили это исправить? Исправить ошибку было легко, а последствия были незначительными, но это лишь в этот раз. Проблемы больше, их сложнее исправить, и они обходятся дороже, чем дальше в процессе разработки вы продвигаетесь. Но источник проблем обычно один и тот же: требования были неясными, непоследовательными или неправильными.
ИИ сейчас: шахматы против беспилотных автомобилей
Концепция ИИ существует уже довольно давно. Он был применён в шахматах ещё в 80-х годах. К концу 90-х ИИ превзошёл возможности человека выигрывать в шахматы. Это тоже неудивительно, ведь параметры шахмат КОНЕЧНЫ. При каждом ходе существует конечное число возможных ходов. ИИ может рассчитывать последствия каждого хода, чтобы выбрать наиболее подходящий, приводящий в итоге к победе.
Другая сфера - беспилотные автомобили. Производители уже довольно давно их обещают. Они в основном используют механизмы, основанные на правилах, для принятия решений. Но здесь, в отличие от шахмат, правила поведения во всех возможных ситуациях чётко не определены. Водители в поездке принимают тысячи мелких решений. Правильность этих решений часто означает разницу между прибытием в пункт назначения и аварией.
В сфере технологий есть понятие для выражения доступности сервиса в виде количества девяток. Стандартом сейчас является 5 девяток, т.е. доступность 99,999% времени. Проблема в том, что достичь уровня доступности в 99% легко, но каждая следующая девятка даётся сложнее в геометрической прогрессии.
Причина, по которой так сложно достичь приемлемого уровня безопасности беспилотных авто в том, что вождение автомобиля предполагает значительно больше переменных, чем шахматы, и эти переменные НЕ КОНЕЧНЫ. Первые 95% или 99% могут быть предсказуемыми и легко поддающимися учёту. Но дальше существует очень много крайних случаев, которые могут иметь некоторые общие черты, но каждый из которых уникален: другие машины, управляемые людьми, перекрытия дорог, аварии, погодные явления. Невероятно сложно заставить модель ИИ учитывать и распознавать эти аномалии, и, что более важно, правильно реагировать, не попадая в аварию.
Окончание следует…
Источник: https://stackoverflow.blog/2023/12/29/the-hardest-part-of-building-software-is-not-coding-its-requirements/
Автор оригинала: Jared Toporek (консультант в разработке ПО, создатель приложения Keenforms)
👍20
День 1810. #Оффтоп
Самая Сложная Часть Создания ПО — не Кодирование, а ТЗ. Окончание
Начало
ИИ не может создавать ПО, только код
Создание и поддержка ПО имеет гораздо больше общего с вождением автомобиля, чем с игрой в шахматы. Здесь больше переменных, и правила основаны на суждениях. Когда вы создаёте ПО, у вас может быть желаемый результат, но маловероятно, что он будет таким же строго рассчитанным, как в шахматах. ПО редко создаётся раз и навсегда. Добавляются функции и исправляются ошибки — это постоянная эволюция.
В разработке есть инструмент, позволяющий приблизить наши проекты к строго контролируемому механизму правил шахмат: технические задания. В лучшем случае ТЗ описывает ожидаемое поведение пользователей и ход выполнения программы. Чтобы купить товар, пользователь нажимает эту кнопку, создаёт эту структуру данных, запускает этот сервис. Однако чаще нам вручают списки пожеланий функций, макеты на салфетке и нечёткие документы, и говорят, чтобы мы из этого сделали что-то рабочее. Хуже того, требования меняются или игнорируются.
Недавно меня попросили помочь команде создать опросник для помощи людям по вопросам здоровья. Приложение должно было работать в регионах без надёжного интернета, поэтому решено было проводить опросы через SMS. Как только я начал слушать описание требований, я понял, что это будет проблемой. Одно дело, когда компания спрашивает вас по шкале от 1 до 10, насколько вероятно, что вы снова воспользуетесь их сервисом. Совсем другое дело — создать многоэтапный опрос с вопросами с несколькими вариантами ответов о симптомах. Я не отказался, но упомянул все возможные точки сбоя в этом процессе и хотел, чтобы команда чётко определила, как мы будем обрабатывать поступающие ответы на все вопросы. Номера вариантов ответа через запятую? Что если число в ответе не соответствует ни одному из вариантов?
После всех этих вопросов команда пришла к выводу: лучше не делать это приложение. Как ни странно, я бы сказал, что это успешный результат работы. Было бы более расточительно продолжать работу без чёткого разрешения всех потенциальных ошибок. Сможет ли ИИ так же разубедить клиентов создавать приложение? И будет ли вообще задавать наводящие вопросы, предупреждая обо всех возможных проблемах?
Чтобы создать функционирующее ПО с помощью ИИ, необходимо знать, чего вы хотите, и уметь ясно и точно это определить. Бывают случаи, когда я пишу ПО только для себя и не осознаю некоторых трудностей и проблем, пока фактически не начну писать код.
За последнее десятилетие индустрия программного обеспечения перешла от каскадной методологии (Waterfall) к гибкой (Agile). Waterfall точно определяет, что вы хотите, ещё до написания кода, а Agile обеспечивает достаточную гибкость, чтобы вы могли вносить изменения по ходу.
Много проектов, использующих Waterfall, потерпели неудачу, потому что люди думали, что знают, чего хотят, и могут точно описать это, но были разочарованы в конечном продукте.
Возможно, ИИ лучше подойдёт для переписывания уже имеющегося ПО, под новое оборудование или новые версии языков и фреймворков. По-прежнему существует гигантское количество ПО, написанного на устаревшем языке и всё меньше людей этот язык изучающих. Если вы точно знаете, чего хотите, возможно, вы сможете заставить ИИ производить ПО быстрее и дешевле, чем команда программистов-людей. Я верю, что ИИ сможет сделать уже созданное ПО быстрее, чем программисты-люди, но это потому, что кто-то по ходу разработки понял, что это ПО должно делать.
ИИ может неплохо справляться с созданием ПО по модели Waterfall. Проблема в этой модели - в людях. Не там, где подписанные ТЗ передаются команде программистов для написания кода, а во всём том, что было до этого. ИИ может делать некоторые необычные вещи, но он не может читать ваши мысли или подсказывать, чего вам следует хотеть.
Источник: https://stackoverflow.blog/2023/12/29/the-hardest-part-of-building-software-is-not-coding-its-requirements/
Автор оригинала: Jared Toporek (консультант в разработке ПО, создатель приложения Keenforms).
Самая Сложная Часть Создания ПО — не Кодирование, а ТЗ. Окончание
Начало
ИИ не может создавать ПО, только код
Создание и поддержка ПО имеет гораздо больше общего с вождением автомобиля, чем с игрой в шахматы. Здесь больше переменных, и правила основаны на суждениях. Когда вы создаёте ПО, у вас может быть желаемый результат, но маловероятно, что он будет таким же строго рассчитанным, как в шахматах. ПО редко создаётся раз и навсегда. Добавляются функции и исправляются ошибки — это постоянная эволюция.
В разработке есть инструмент, позволяющий приблизить наши проекты к строго контролируемому механизму правил шахмат: технические задания. В лучшем случае ТЗ описывает ожидаемое поведение пользователей и ход выполнения программы. Чтобы купить товар, пользователь нажимает эту кнопку, создаёт эту структуру данных, запускает этот сервис. Однако чаще нам вручают списки пожеланий функций, макеты на салфетке и нечёткие документы, и говорят, чтобы мы из этого сделали что-то рабочее. Хуже того, требования меняются или игнорируются.
Недавно меня попросили помочь команде создать опросник для помощи людям по вопросам здоровья. Приложение должно было работать в регионах без надёжного интернета, поэтому решено было проводить опросы через SMS. Как только я начал слушать описание требований, я понял, что это будет проблемой. Одно дело, когда компания спрашивает вас по шкале от 1 до 10, насколько вероятно, что вы снова воспользуетесь их сервисом. Совсем другое дело — создать многоэтапный опрос с вопросами с несколькими вариантами ответов о симптомах. Я не отказался, но упомянул все возможные точки сбоя в этом процессе и хотел, чтобы команда чётко определила, как мы будем обрабатывать поступающие ответы на все вопросы. Номера вариантов ответа через запятую? Что если число в ответе не соответствует ни одному из вариантов?
После всех этих вопросов команда пришла к выводу: лучше не делать это приложение. Как ни странно, я бы сказал, что это успешный результат работы. Было бы более расточительно продолжать работу без чёткого разрешения всех потенциальных ошибок. Сможет ли ИИ так же разубедить клиентов создавать приложение? И будет ли вообще задавать наводящие вопросы, предупреждая обо всех возможных проблемах?
Чтобы создать функционирующее ПО с помощью ИИ, необходимо знать, чего вы хотите, и уметь ясно и точно это определить. Бывают случаи, когда я пишу ПО только для себя и не осознаю некоторых трудностей и проблем, пока фактически не начну писать код.
За последнее десятилетие индустрия программного обеспечения перешла от каскадной методологии (Waterfall) к гибкой (Agile). Waterfall точно определяет, что вы хотите, ещё до написания кода, а Agile обеспечивает достаточную гибкость, чтобы вы могли вносить изменения по ходу.
Много проектов, использующих Waterfall, потерпели неудачу, потому что люди думали, что знают, чего хотят, и могут точно описать это, но были разочарованы в конечном продукте.
Возможно, ИИ лучше подойдёт для переписывания уже имеющегося ПО, под новое оборудование или новые версии языков и фреймворков. По-прежнему существует гигантское количество ПО, написанного на устаревшем языке и всё меньше людей этот язык изучающих. Если вы точно знаете, чего хотите, возможно, вы сможете заставить ИИ производить ПО быстрее и дешевле, чем команда программистов-людей. Я верю, что ИИ сможет сделать уже созданное ПО быстрее, чем программисты-люди, но это потому, что кто-то по ходу разработки понял, что это ПО должно делать.
ИИ может неплохо справляться с созданием ПО по модели Waterfall. Проблема в этой модели - в людях. Не там, где подписанные ТЗ передаются команде программистов для написания кода, а во всём том, что было до этого. ИИ может делать некоторые необычные вещи, но он не может читать ваши мысли или подсказывать, чего вам следует хотеть.
Источник: https://stackoverflow.blog/2023/12/29/the-hardest-part-of-building-software-is-not-coding-its-requirements/
Автор оригинала: Jared Toporek (консультант в разработке ПО, создатель приложения Keenforms).
👍19
День 1811. #ЗаметкиНаПолях
Слабые События в C#
События являются распространённым источником утечек памяти, когда подписчик забывает отписаться от события, а жизненный цикл подписчика и издателя события разный. Можно попробовать использовать слабые события. Это события, которые не мешают сборщику мусора удалять подписчика.
Слабые события работают на основе WeakReference<T> — ссылки на объект, которая не предотвращает сбор мусора для объекта. В примере ниже мы храним как слабые ссылки на события. Также мы сохраняем делегаты обработчиков в привязке к объекту обработчика в ConditionalWeakTable. Это предотвращает преждевременное удаление делегатов сборщиком мусора.
Вот пример, как использовать класс WeakEvent<TEventArgs>:
Источник: https://www.meziantou.net/weak-events-in-csharp.htm
Слабые События в C#
События являются распространённым источником утечек памяти, когда подписчик забывает отписаться от события, а жизненный цикл подписчика и издателя события разный. Можно попробовать использовать слабые события. Это события, которые не мешают сборщику мусора удалять подписчика.
Слабые события работают на основе WeakReference<T> — ссылки на объект, которая не предотвращает сбор мусора для объекта. В примере ниже мы храним как слабые ссылки на события. Также мы сохраняем делегаты обработчиков в привязке к объекту обработчика в ConditionalWeakTable. Это предотвращает преждевременное удаление делегатов сборщиком мусора.
class WeakEvent<TEventArgs>
{
private ImmutableList<
WeakReference<EventHandler<TEventArgs>>>
_listeners =
ImmutableList<
WeakReference<
EventHandler<TEventArgs>>>.Empty;
private readonly
ConditionalWeakTable<object, List<object>>
_delegates = [];
public void AddListener(
EventHandler<TEventArgs> handler)
{
if (handler == null)
return;
var weakRef = new
WeakReference<EventHandler<TEventArgs>>(handler);
_listeners = _listeners.Add(weakRef);
if (handler.Target != null)
_delegates
.GetOrCreateValue(handler.Target)
.Add(handler);
}
public void RemoveListener(
EventHandler<TEventArgs> handler)
{
if (handler == null)
return;
_listeners = _listeners
.RemoveAll(wr =>
!wr.TryGetTarget(out var target)
|| handler.Equals(target));
if (handler.Target != null
&& _delegates.TryGetValue(
handler.Target, out var weakRef))
{
weakRef.Remove(handler);
}
}
public void Raise(object? sender, TEventArgs args)
{
foreach (var l in _listeners)
{
if (l.TryGetTarget(out var target))
target.Invoke(sender, args);
else
{
// Удаляем делегат, если обработчик был удалён
_listeners = _listeners.Remove(l);
}
}
}
}
Вот пример, как использовать класс WeakEvent<TEventArgs>:
var sample = new Sample();
sample.CustomEvent +=
(sender, e) => Console.WriteLine("handler");
sample.DoAction();
class Sample
{
private readonly WeakEvent<EventArgs>
_event = new();
public event EventHandler<EventArgs> CustomEvent
{
add =>
_event.AddListener(value);
remove =>
_event.RemoveListener(value);
}
public void DoAction()
{
_event.Raise(this, EventArgs.Empty);
}
}
Источник: https://www.meziantou.net/weak-events-in-csharp.htm
👍9
День 1812. #TipsAndTricks
Используем Оператор Неявного Преобразования для Улучшения Читаемости
Оператор неявного преобразования — одна из самых крутых скрытых функций C#. И причина, по которой он не так популярен, заключается в том, что трудно придумать, как его использовать. Многие называют его неявным конструктором, понятно, откуда это взялось. Но давайте попробовать использовать его так, как Microsoft его задумали.
Оператор неявного преобразования позволяет нам без лишних усилий преобразовывать один тип в другой. Например:
У нас есть класс Color, который просто принимает в конструкторе шестнадцатеричный код цвета. Также есть класс Wall с методом Paint, который получает цвет.
Использование:
Мы создаём экземпляр класса Wall и вызываем метод Paint. Однако мы передаём строку, а не экземпляр Color. Как это возможно? Всё благодаря магии неявного оператора приведения. Если вы посмотрите на определение оператора в классе Color, вы увидите, что он вызывает конструктор, используя предоставленный шестнадцатеричный код.
Это очень простой сценарий, но его потенциал огромен. Например, вы можете использовать его для номеров паспортов, координат, URI, IP-адресов и многих других объектов-значений, элегантно решая проблему одержимости примитавами. Только не переусердствуйте и не используйте сверхсложные операторы.
Источник: https://intodot.net/using-implicit-conversion-operators-in-c-to-improve-readability/
Используем Оператор Неявного Преобразования для Улучшения Читаемости
Оператор неявного преобразования — одна из самых крутых скрытых функций C#. И причина, по которой он не так популярен, заключается в том, что трудно придумать, как его использовать. Многие называют его неявным конструктором, понятно, откуда это взялось. Но давайте попробовать использовать его так, как Microsoft его задумали.
Оператор неявного преобразования позволяет нам без лишних усилий преобразовывать один тип в другой. Например:
class Color
{
public string HexCode { get; set; }
public Color(string hexCode)
{
HexCode = hexCode;
}
public static implicit operator
Color(string hexCode) => new(hexCode);
}
class Wall
{
public void Paint(Color color) =>
Console.WriteLine($"Цвет: {color.HexCode}");
}
У нас есть класс Color, который просто принимает в конструкторе шестнадцатеричный код цвета. Также есть класс Wall с методом Paint, который получает цвет.
Использование:
var wall = new Wall();
wall.Paint("#FF0000");
Мы создаём экземпляр класса Wall и вызываем метод Paint. Однако мы передаём строку, а не экземпляр Color. Как это возможно? Всё благодаря магии неявного оператора приведения. Если вы посмотрите на определение оператора в классе Color, вы увидите, что он вызывает конструктор, используя предоставленный шестнадцатеричный код.
Это очень простой сценарий, но его потенциал огромен. Например, вы можете использовать его для номеров паспортов, координат, URI, IP-адресов и многих других объектов-значений, элегантно решая проблему одержимости примитавами. Только не переусердствуйте и не используйте сверхсложные операторы.
Источник: https://intodot.net/using-implicit-conversion-operators-in-c-to-improve-readability/
👍20
День 1813. #Testing
Практикуем TDD
Люди не пришли к единому мнению относительно определения процесса разработки через тестирование (TDD). Сегодня рассмотрим процесс Cannon TDD, предложенный Кентом Бэком. Если вы делаете что-то по-другому и это вам подходит, отлично!
TDD — это рабочий процесс написания ПО. Программисту необходимо изменить поведение системы (или создать её). TDD призван помочь создать новое состояние системы, в котором:
- Всё, что раньше работало, продолжает работать.
- Новое поведение работает так, как ожидалось.
- Система готова к следующим изменениям.
- Программист и его коллеги уверены в вышеперечисленных пунктах.
Разделение интерфейса/реализации
Первое недопонимание заключается в том, что люди смешивают всю разработку в одну кучу. Есть два сценария:
- Как вызывается конкретная часть поведения.
- Как система реализует такое поведение.
Шаги
Люди — паршивые компьютеры. Следующий процесс похож на алгоритм, но это не так. Он написан так в попытке эффективно общаться с людьми, привыкшими работать с программами. «Попытке», потому что люди склонны говорить: «TDD - отстой! Я сделал <совершенно другое>, и это провалилось».
1. Список тестов
Составьте список всех ожидаемых вариантов нового поведения:
- базовый случай,
- что, если время ожидания истечет,
- что, если ключа нет в БД,
и т.п.
Это поведенческий анализ. Вы думаете обо всех случаях, в которых изменение поведения должно сработать. Если вы думаете, как изменение поведения не должно нарушить существующее поведение, добавьте и это.
Ошибка: вносить детали реализации. Нет. Позже будет достаточно времени, чтобы решить, как будут выглядеть внутренности. Вы сможете лучше составить список тестов, если поведение - всё, на чём вы сосредоточитесь.
2. Напишите тест
Один. По-настоящему автоматизированный тест с настройкой, вызовом и утверждениями (совет: попробуйте начинать с утверждений). Именно сейчас вы начнёте принимать проектные решения, в первую очередь решения по интерфейсу. Некоторые решения по реализации могут просочиться, но со временем вы научитесь избегать этого.
Ошибки:
1) Писать тесты без утверждений только для того, чтобы было покрытие.
2) Писать сразу все тесты для списка, а затем заставлять их проходить по одному. Что будет, если проход 1го теста заставит вас пересмотреть решение, которое повлияет на остальные тесты? Переписывание всего, депрессия и/или скука. Выбор следующего теста — важный навык, который приходит только с опытом. Порядок тестов может существенно повлиять как на опыт программирования, так и на конечный результат.
3. Заставьте тест пройти
Измените систему так, чтобы тест прошёл успешно.
Ошибки:
1) Удалять утверждения, чтобы тест казался пройденным.
2) Копировать фактически вычисленные значения в ожидаемые значения теста. Это исключает двойную проверку, которая создает большую часть ценности TDD.
3) Смешивать рефакторинг с прохождением теста. Заставьте тест работать, а затем исправьте его. Ваш мозг (в конце концов) скажет вам спасибо.
Если в процессе вы обнаружите необходимость нового теста, добавьте его в список тестов. Если тест заставляет изменить решения и переписать предыдущие тесты, решите, продолжать или начать заново (совет: начните сначала, но выберите другой порядок реализации тестов). Когда тест пройден, вычеркните его из списка.
4. Рефакторинг
При необходимости.
Ошибки:
1) Переписывать больше, чем нужно.
2) Вводить абстракции слишком рано. Дублирование кода — это подсказка, а не приказ к действию.
5. Если список тестов не пуст, перейти к п. 2.
Продолжайте тестировать и писать код, пока ваш страх перед поведением кода не превратится в скуку.
Источник: https://tidyfirst.substack.com/p/canon-tdd
Практикуем TDD
Люди не пришли к единому мнению относительно определения процесса разработки через тестирование (TDD). Сегодня рассмотрим процесс Cannon TDD, предложенный Кентом Бэком. Если вы делаете что-то по-другому и это вам подходит, отлично!
TDD — это рабочий процесс написания ПО. Программисту необходимо изменить поведение системы (или создать её). TDD призван помочь создать новое состояние системы, в котором:
- Всё, что раньше работало, продолжает работать.
- Новое поведение работает так, как ожидалось.
- Система готова к следующим изменениям.
- Программист и его коллеги уверены в вышеперечисленных пунктах.
Разделение интерфейса/реализации
Первое недопонимание заключается в том, что люди смешивают всю разработку в одну кучу. Есть два сценария:
- Как вызывается конкретная часть поведения.
- Как система реализует такое поведение.
Шаги
Люди — паршивые компьютеры. Следующий процесс похож на алгоритм, но это не так. Он написан так в попытке эффективно общаться с людьми, привыкшими работать с программами. «Попытке», потому что люди склонны говорить: «TDD - отстой! Я сделал <совершенно другое>, и это провалилось».
1. Список тестов
Составьте список всех ожидаемых вариантов нового поведения:
- базовый случай,
- что, если время ожидания истечет,
- что, если ключа нет в БД,
и т.п.
Это поведенческий анализ. Вы думаете обо всех случаях, в которых изменение поведения должно сработать. Если вы думаете, как изменение поведения не должно нарушить существующее поведение, добавьте и это.
Ошибка: вносить детали реализации. Нет. Позже будет достаточно времени, чтобы решить, как будут выглядеть внутренности. Вы сможете лучше составить список тестов, если поведение - всё, на чём вы сосредоточитесь.
2. Напишите тест
Один. По-настоящему автоматизированный тест с настройкой, вызовом и утверждениями (совет: попробуйте начинать с утверждений). Именно сейчас вы начнёте принимать проектные решения, в первую очередь решения по интерфейсу. Некоторые решения по реализации могут просочиться, но со временем вы научитесь избегать этого.
Ошибки:
1) Писать тесты без утверждений только для того, чтобы было покрытие.
2) Писать сразу все тесты для списка, а затем заставлять их проходить по одному. Что будет, если проход 1го теста заставит вас пересмотреть решение, которое повлияет на остальные тесты? Переписывание всего, депрессия и/или скука. Выбор следующего теста — важный навык, который приходит только с опытом. Порядок тестов может существенно повлиять как на опыт программирования, так и на конечный результат.
3. Заставьте тест пройти
Измените систему так, чтобы тест прошёл успешно.
Ошибки:
1) Удалять утверждения, чтобы тест казался пройденным.
2) Копировать фактически вычисленные значения в ожидаемые значения теста. Это исключает двойную проверку, которая создает большую часть ценности TDD.
3) Смешивать рефакторинг с прохождением теста. Заставьте тест работать, а затем исправьте его. Ваш мозг (в конце концов) скажет вам спасибо.
Если в процессе вы обнаружите необходимость нового теста, добавьте его в список тестов. Если тест заставляет изменить решения и переписать предыдущие тесты, решите, продолжать или начать заново (совет: начните сначала, но выберите другой порядок реализации тестов). Когда тест пройден, вычеркните его из списка.
4. Рефакторинг
При необходимости.
Ошибки:
1) Переписывать больше, чем нужно.
2) Вводить абстракции слишком рано. Дублирование кода — это подсказка, а не приказ к действию.
5. Если список тестов не пуст, перейти к п. 2.
Продолжайте тестировать и писать код, пока ваш страх перед поведением кода не превратится в скуку.
Источник: https://tidyfirst.substack.com/p/canon-tdd
👍12👎5