День девятьсот восемьдесят первый. #Оффтоп #CodeReview
Обзоры Кода. Передовой Опыт. Окончание
Начало
Продолжение
На что обращать внимание при обзоре кода
Многие аспекты проверки кода можно автоматизировать, но человеческие навыки и опыт остаются критичными для обнаружения важных улучшений:
1. Ошибки требований
Разработчик мог неправильно понять некоторые требования. Его код может быть протестирован на 100%, но, если и код, и написанные тесты основаны на неправильных предположениях, задача не может считаться выполненной.
2. Изменения пользовательского интерфейса
Создание правильного UI, который сделает пользователей продуктивными, - одна из самых сложных задач разработки. Проверка кода и проверка UI специалистами команды являются обязательными, чтобы убедиться, что продукт развивается в правильном направлении.
3. Трудно находимые ошибки
Ошибки, связанные с параллельной обработкой (взаимоблокировки, состояния гонки и т.п.), трудно обнаружить, трудно воспроизвести и трудно исправить. Младшие разработчики часто их допускают, даже не подозревая, что они могут произойти. При обзоре кода опытный программист сможет оценить, возможно ли возникновение ошибки. Например, он проверит изменение общего состояния и чистоту метода. Также могут быть обнаружены некоторые другие сложные ошибки, включая утечку памяти и проблемы производительности, вроде проблемы N+1 запроса.
4. Уязвимости
Существует широкий спектр потенциальных ловушек безопасности, требующих специальных знаний. Некоторые инструменты, такие как Semmle, могут автоматически обнаруживать многие из этих подводных камней. Но если безопасность вас беспокоит (а она должна беспокоить), необходимы регулярные обзоры кода, проводимые экспертами по безопасности.
5. Именование
Для обеспечения соблюдения простых требований к именованию могут быть написаны автоматические правила. Также инструментальные средства могут заставить использовать предопределённую терминологию основного домена. Кроме того, правильное именование особенно важно для публичных идентификаторов API. Вот почему требуется тщательный анализ именования человеком.
6. Тестирование
Тестирование можно измерить и в значительной степени автоматизировать. Но коэффициента покрытия кода и количества пройденных тестов недостаточно. Человек должен просмотреть тесты, чтобы убедиться в правильности установленных условий и в том, что тесты не слишком сложны.
7. Последовательность
Часто молодые участники команды заново изобретают колесо, потому что не знакомы с общекорпоративными шаблонами и библиотеками. Некоторые автоматические правила могут быть написаны для принудительного использования определённых классов в определённых ситуациях. Но обзоры кода представляют собой отличный способ рассказать о культуре и базе знаний компании.
8. Комментарий
Комментарии должны быть написаны тщательно на правильном человеческом языке, чтобы объяснить, почему этот код написан и почему написан именно таким образом. Например, когда какой-то нетривиальный код адаптируется из ответа со Stackoverflow, стоит указать URL ответа и контекст. С другой стороны, код должен быть достаточно читабельным, чтобы не нужно было комментариев, объясняющих, что он делает.
9. Документация
Инструменты могут легко обнаружить недостающую документацию. IDE, вроде Visual Studio, может легко сгенерировать скелет класса или документацию метода. Но документация пишется человеком и для человека. Чтобы она была действительно информативной, необходима надлежащая проверка со стороны человека. Мы, разработчики, регулярно разочаровываемся в поверхностной и устаревшей документации, которая на самом деле не объясняет, зачем нужен API, как его следует использовать и чего от него ожидать. Правильная документация - отличный способ снизить затраты на поддержку и снизить разочарование пользователей.
Источник: https://blog.ndepend.com/what-is-code-review-guidelines-best-practices/
Обзоры Кода. Передовой Опыт. Окончание
Начало
Продолжение
На что обращать внимание при обзоре кода
Многие аспекты проверки кода можно автоматизировать, но человеческие навыки и опыт остаются критичными для обнаружения важных улучшений:
1. Ошибки требований
Разработчик мог неправильно понять некоторые требования. Его код может быть протестирован на 100%, но, если и код, и написанные тесты основаны на неправильных предположениях, задача не может считаться выполненной.
2. Изменения пользовательского интерфейса
Создание правильного UI, который сделает пользователей продуктивными, - одна из самых сложных задач разработки. Проверка кода и проверка UI специалистами команды являются обязательными, чтобы убедиться, что продукт развивается в правильном направлении.
3. Трудно находимые ошибки
Ошибки, связанные с параллельной обработкой (взаимоблокировки, состояния гонки и т.п.), трудно обнаружить, трудно воспроизвести и трудно исправить. Младшие разработчики часто их допускают, даже не подозревая, что они могут произойти. При обзоре кода опытный программист сможет оценить, возможно ли возникновение ошибки. Например, он проверит изменение общего состояния и чистоту метода. Также могут быть обнаружены некоторые другие сложные ошибки, включая утечку памяти и проблемы производительности, вроде проблемы N+1 запроса.
4. Уязвимости
Существует широкий спектр потенциальных ловушек безопасности, требующих специальных знаний. Некоторые инструменты, такие как Semmle, могут автоматически обнаруживать многие из этих подводных камней. Но если безопасность вас беспокоит (а она должна беспокоить), необходимы регулярные обзоры кода, проводимые экспертами по безопасности.
5. Именование
Для обеспечения соблюдения простых требований к именованию могут быть написаны автоматические правила. Также инструментальные средства могут заставить использовать предопределённую терминологию основного домена. Кроме того, правильное именование особенно важно для публичных идентификаторов API. Вот почему требуется тщательный анализ именования человеком.
6. Тестирование
Тестирование можно измерить и в значительной степени автоматизировать. Но коэффициента покрытия кода и количества пройденных тестов недостаточно. Человек должен просмотреть тесты, чтобы убедиться в правильности установленных условий и в том, что тесты не слишком сложны.
7. Последовательность
Часто молодые участники команды заново изобретают колесо, потому что не знакомы с общекорпоративными шаблонами и библиотеками. Некоторые автоматические правила могут быть написаны для принудительного использования определённых классов в определённых ситуациях. Но обзоры кода представляют собой отличный способ рассказать о культуре и базе знаний компании.
8. Комментарий
Комментарии должны быть написаны тщательно на правильном человеческом языке, чтобы объяснить, почему этот код написан и почему написан именно таким образом. Например, когда какой-то нетривиальный код адаптируется из ответа со Stackoverflow, стоит указать URL ответа и контекст. С другой стороны, код должен быть достаточно читабельным, чтобы не нужно было комментариев, объясняющих, что он делает.
9. Документация
Инструменты могут легко обнаружить недостающую документацию. IDE, вроде Visual Studio, может легко сгенерировать скелет класса или документацию метода. Но документация пишется человеком и для человека. Чтобы она была действительно информативной, необходима надлежащая проверка со стороны человека. Мы, разработчики, регулярно разочаровываемся в поверхностной и устаревшей документации, которая на самом деле не объясняет, зачем нужен API, как его следует использовать и чего от него ожидать. Правильная документация - отличный способ снизить затраты на поддержку и снизить разочарование пользователей.
Источник: https://blog.ndepend.com/what-is-code-review-guidelines-best-practices/
День девятьсот восемьдесят второй. #ЗаметкиНаПолях #AsyncTips
Передача информации о ходе выполнения операции
Задача: отреагировать на прогресс выполнения операции.
Решение:
Используйте
Лучше определить
Заметьте, что, если метод поддерживает уведомления о прогрессе, лучше, чтоб он также поддерживал отмену.
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
Передача информации о ходе выполнения операции
Задача: отреагировать на прогресс выполнения операции.
Решение:
Используйте
IProgress<T>
и Progress<T>
. Ваш асинхронный метод должен получать аргумент IProgress<T>
; здесь T
— тип прогресса, о котором вы хотите сообщать:async Task MyMethodAsync(IProgress<double> progress = null)Пример использования в вызывающем коде:
{
bool done = false;
double completed = 0;
while (!done) {
...
progress?.Report(completed);
}
}
async Task CallMyMethodAsync()По действующим соглашениям параметр
{
var progress = new Progress<double>(
p => progressBar.Value = p
);
await MyMethodAsync(progress);
}
IProgress<T>
может быть равен null
, если вызывающей стороне не нужны уведомления о прогрессе. Включите соответствующую проверку в асинхронный метод.Лучше определить
T
как неизменяемый тип (или тип-значение). Если T
является изменяемым ссылочным типом, то вам придётся самостоятельно создавать отдельную копию при каждом вызове IProgress<T>.Report
.Progress<T>
сохраняет текущий контекст при создании и активизирует свой обратный вызов в этом контексте. Это означает, что если Progress<T>
конструируется в UI-потоке (как в методе CallMyMethodAsync
выше), то вы сможете обновить пользовательский интерфейс из его обратного вызова, даже если асинхронный метод вызывает Report
из фонового потока.Заметьте, что, если метод поддерживает уведомления о прогрессе, лучше, чтоб он также поддерживал отмену.
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
👍1
День девятьсот восемьдесят третий. #юмор
Не знаю, как с этим в последних версиях .NET, но в .NET Framework приходилось помучиться. Не до пересоздания проекта, конечно, но...
Не знаю, как с этим в последних версиях .NET, но в .NET Framework приходилось помучиться. Не до пересоздания проекта, конечно, но...
День девятьсот восемьдесят четвёртый. #ЗаметкиНаПолях
Фигурные Скобки и Область Видимости Переменных
Даже если вы опытный разработчик C#, есть функции, о которых вы могли не знать. Например, в C# вы можете объявлять переменные в блоках кода в фигурных скобках для определения методов, условных операторов, циклов и т.п. Например, фигурные скобки определяют область видимости переменной в методе:
Что произойдет, если мы добавим следующий оператор вне блока, как в следующем примере?
CS0103: The name 'theVar' does not exist in the current context
(Имя 'theVar' не существует в текущем контексте)
Как видите, переменная, определённая внутри блока кода, недоступна вне его.
Другими словами, вы можете создать область видимости переменной, просто используя фигурные скобки в любом месте вашего кода. Эта функция может быть полезна, когда у вас есть короткая последовательность операторов, которым необходимо передать значение через переменную. Вы создаёте блок кода на лету, объявляете переменную и используете её. Однако предлагаю не злоупотреблять этим. Иногда лучше создать метод и вызвать его. Это может быть более читабельным, чем безымянный блок кода.
Источник: https://auth0.com/blog/five-csharp-features-you-dont-know/
Фигурные Скобки и Область Видимости Переменных
Даже если вы опытный разработчик C#, есть функции, о которых вы могли не знать. Например, в C# вы можете объявлять переменные в блоках кода в фигурных скобках для определения методов, условных операторов, циклов и т.п. Например, фигурные скобки определяют область видимости переменной в методе:
static void Main(string[] args)Также вы можете определить область видимости внутри оператора if:
{
var name = Console.ReadLine();
…
}
if (…)… или while:
{
var dayOfWeek = DateTime.Today.ToString("dddd");
…
}
while (…)Независимо от контекста фигурные скобки ограничивают область действия переменной. Знаете ли вы, что с помощью фигурных скобок можно определить область видимости переменной даже без специального оператора?
{
var dayOfWeek = DateTime.Today.ToString("dddd");
…
}
static void Main(string[] args)Здесь у нас есть блок кода, выделенный фигурными скобками внутри метода
{
Console.WriteLine("Hello");
{
var theVar = "World";
Console.WriteLine(theVar);
}
}
Main()
. Блок кода определяет и инициализирует переменную theVar
и выводит её значение в консоль. Обратите внимание, что нет if, while, for или другого оператора перед фигурными скобками.Что произойдет, если мы добавим следующий оператор вне блока, как в следующем примере?
static void Main(string[] args)Если вы попытаетесь запустить эту программу, вы получите следующую ошибку:
{
Console.WriteLine("Hello");
{
var theVar = "World";
Console.WriteLine(theVar);
}
//оператор вне блока
Console.WriteLine(theVar);
}
CS0103: The name 'theVar' does not exist in the current context
(Имя 'theVar' не существует в текущем контексте)
Как видите, переменная, определённая внутри блока кода, недоступна вне его.
Другими словами, вы можете создать область видимости переменной, просто используя фигурные скобки в любом месте вашего кода. Эта функция может быть полезна, когда у вас есть короткая последовательность операторов, которым необходимо передать значение через переменную. Вы создаёте блок кода на лету, объявляете переменную и используете её. Однако предлагаю не злоупотреблять этим. Иногда лучше создать метод и вызвать его. Это может быть более читабельным, чем безымянный блок кода.
Источник: https://auth0.com/blog/five-csharp-features-you-dont-know/
День девятьсот восемьдесят пятый. #ЗаметкиНаПолях
Правильное Использование Ключевого Слова static в C#
Основная проблема в том, что static плохо сочетается с изменяемым состоянием, т.е. состоянием, которое может изменяться в течение жизненного цикла программы. Как следствие правила использования следующие:
1. Статические поля должны быть только для чтения (по возможности, константами).
2. Статические поля должны ссылаться на неизменяемое состояние.
3. Статические методы должны быть чистыми функциями, то есть вычислять результат из входных параметров, без изменения состояния входных данных или любого другого состояния.
4. Статические классы допустимы, если их поля и методы соответствуют этим рекомендациям.
Основная мотивация для создания статического поля - создать объект, который живет в течение всего времени выполнения программы. Часто это важный объект, например контекст, доступ к которому осуществляется многими методами. На первый взгляд, сделать его статическим и видимым всем удобнее, чем передавать везде как параметр. Посмотрим, какие проблемы это сулит.
1. Код должен выполняться в пределах сессии
Объект, живущий всё время, пока исполняется программа, зачастую не нужен. На самом деле состояние требуется в пределах «сессии»: в течение веб-запроса или во время выполнения тестов. При этом совместное использование изменяемого состояния – не лучшая идея. Все «сессии» теперь должны заботиться о том, чтобы общее состояние было правильно инициализировано и правильно финализировано. Кроме того, веб-запросы де-факто выполняются конкурентно. Даже тесты можно запустить параллельно. В такой ситуации вам необходимо синхронизировать доступ к статическому изменяемому состоянию. А синхронизация доступа - это не только боль, но и убийца производительности.
2. Управление состоянием в конкурентной среде
В этом случае использование одного ресурса на поток устраняет необходимость в синхронизации. Идея в том, что к объекту всегда обращается один и тот же поток, т.е. каждый поток имеет свой собственный экземпляр. Вы можете использовать для этого статическое поле, помеченное атрибутом
3. Глобальное состояние и поддержка
Статические поля представляют глобальное состояние, доступ к которому можно получить из любого места кода. И разработчики могут создать зависимость от глобального состояния где угодно. Со временем это неизбежно приведёт к неуправляемому доступу к глобальному состоянию, и результатом будет подверженный ошибкам код, неконтролируемые побочные эффекты и утомительные сеансы отладки.
4. Чистые функции
Часто классы содержат функции, которые вычисляют результат без использования полей экземпляра объекта. Эти функции могут быть объявлены статическими. В результате получаются жирные классы, которые трудно понять и поддерживать. Поэтому в случае сложной логики лучше размещать такую реализацию во вспомогательных статических классах. Это не только делает классы экземпляров меньше и понятнее, но и сами статические классы хорошо поддаются тестированию: тесту нужно инициализировать входные данные, вызвать статический метод и проверить результат. Нет никакого скрытого состояния, о котором стоит беспокоиться, не нужно создавать объекты-имитации.
А методы расширения помогают не только отделить объект от его поведения, но и сделать вызовы более естественными.
Итого
Ключевое слово static плохо сочетается с изменяемым состоянием. Неправильное использование обязательно приведёт к серьёзным неприятностям. Но оно может стать серьёзным подспорьем, если использовать его с умом.
Источник: https://blog.ndepend.com/the-proper-usages-of-the-keyword-static-in-c/
Правильное Использование Ключевого Слова static в C#
Основная проблема в том, что static плохо сочетается с изменяемым состоянием, т.е. состоянием, которое может изменяться в течение жизненного цикла программы. Как следствие правила использования следующие:
1. Статические поля должны быть только для чтения (по возможности, константами).
2. Статические поля должны ссылаться на неизменяемое состояние.
3. Статические методы должны быть чистыми функциями, то есть вычислять результат из входных параметров, без изменения состояния входных данных или любого другого состояния.
4. Статические классы допустимы, если их поля и методы соответствуют этим рекомендациям.
Основная мотивация для создания статического поля - создать объект, который живет в течение всего времени выполнения программы. Часто это важный объект, например контекст, доступ к которому осуществляется многими методами. На первый взгляд, сделать его статическим и видимым всем удобнее, чем передавать везде как параметр. Посмотрим, какие проблемы это сулит.
1. Код должен выполняться в пределах сессии
Объект, живущий всё время, пока исполняется программа, зачастую не нужен. На самом деле состояние требуется в пределах «сессии»: в течение веб-запроса или во время выполнения тестов. При этом совместное использование изменяемого состояния – не лучшая идея. Все «сессии» теперь должны заботиться о том, чтобы общее состояние было правильно инициализировано и правильно финализировано. Кроме того, веб-запросы де-факто выполняются конкурентно. Даже тесты можно запустить параллельно. В такой ситуации вам необходимо синхронизировать доступ к статическому изменяемому состоянию. А синхронизация доступа - это не только боль, но и убийца производительности.
2. Управление состоянием в конкурентной среде
В этом случае использование одного ресурса на поток устраняет необходимость в синхронизации. Идея в том, что к объекту всегда обращается один и тот же поток, т.е. каждый поток имеет свой собственный экземпляр. Вы можете использовать для этого статическое поле, помеченное атрибутом
ThreadStatic
. Конкурентный доступ при этом исключается, но каждый поток по-прежнему должен беспокоиться об управлении состоянием. Поэтому создание выделенного объекта на поток более элегантно, чем статическое поле с атрибутом ThreadStatic
: так вы можете правильно отслеживать историю изменений состояния.3. Глобальное состояние и поддержка
Статические поля представляют глобальное состояние, доступ к которому можно получить из любого места кода. И разработчики могут создать зависимость от глобального состояния где угодно. Со временем это неизбежно приведёт к неуправляемому доступу к глобальному состоянию, и результатом будет подверженный ошибкам код, неконтролируемые побочные эффекты и утомительные сеансы отладки.
4. Чистые функции
Часто классы содержат функции, которые вычисляют результат без использования полей экземпляра объекта. Эти функции могут быть объявлены статическими. В результате получаются жирные классы, которые трудно понять и поддерживать. Поэтому в случае сложной логики лучше размещать такую реализацию во вспомогательных статических классах. Это не только делает классы экземпляров меньше и понятнее, но и сами статические классы хорошо поддаются тестированию: тесту нужно инициализировать входные данные, вызвать статический метод и проверить результат. Нет никакого скрытого состояния, о котором стоит беспокоиться, не нужно создавать объекты-имитации.
А методы расширения помогают не только отделить объект от его поведения, но и сделать вызовы более естественными.
Select
, Where
, First
и т.п. - это статические методы расширения для IEnumerable
, которые не изменяют входную последовательность, а возвращают новую.Итого
Ключевое слово static плохо сочетается с изменяемым состоянием. Неправильное использование обязательно приведёт к серьёзным неприятностям. Но оно может стать серьёзным подспорьем, если использовать его с умом.
Источник: https://blog.ndepend.com/the-proper-usages-of-the-keyword-static-in-c/
День девятьсот восемьдесят шестой. #Курсы
Бесплатная Неделя на Pluralsight
Налетай! Торопись! Изучай! Левел-апсь!)))
Pluralsight объявили неделю бесплатного доступа ко всем своим курсам. До понедельника, 18 октября 7 утра по Москве все курсы, видео и проекты можно получить абсолютно бесплатно.
Зарегистрироваться можно здесь https://www.pluralsight.com/offer/2021/q4-free-week
Если кому интересно, чтоб не искать, вот ссылки на мою подборку курсов (работают после регистрации на Pluralsight):
- ASP .NET Core (для экзамена 70-486) https://app.pluralsight.com/channels/details/e6a5dd31-af53-4320-b160-7c4e712b06cb
- Azure Fundamentals (для экзамена AZ-900) https://app.pluralsight.com/paths/certificate/microsoft-azure-fundamentals-az-900
- ASP .NET API, Blazor и Azure https://app.pluralsight.com/channels/details/1e5510d8-c21c-4590-b639-08dc23020281
- Прочее https://app.pluralsight.com/channels/details/b5f23d94-9914-45ba-9bd7-23ea00b5769f
Бесплатная Неделя на Pluralsight
Налетай! Торопись! Изучай! Левел-апсь!)))
Pluralsight объявили неделю бесплатного доступа ко всем своим курсам. До понедельника, 18 октября 7 утра по Москве все курсы, видео и проекты можно получить абсолютно бесплатно.
Зарегистрироваться можно здесь https://www.pluralsight.com/offer/2021/q4-free-week
Если кому интересно, чтоб не искать, вот ссылки на мою подборку курсов (работают после регистрации на Pluralsight):
- ASP .NET Core (для экзамена 70-486) https://app.pluralsight.com/channels/details/e6a5dd31-af53-4320-b160-7c4e712b06cb
- Azure Fundamentals (для экзамена AZ-900) https://app.pluralsight.com/paths/certificate/microsoft-azure-fundamentals-az-900
- ASP .NET API, Blazor и Azure https://app.pluralsight.com/channels/details/1e5510d8-c21c-4590-b639-08dc23020281
- Прочее https://app.pluralsight.com/channels/details/b5f23d94-9914-45ba-9bd7-23ea00b5769f
День девятьсот восемьдесят седьмой. #ЗаметкиНаПолях
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Начало
Разработчики, которые недавно начали работать с внедрением зависимостей в ASP.NET Core, часто неверно понимают принципы его работы. Некоторые заблуждения вызывают ошибки, а другие приводят к избыточному коду. Рассмотрим наиболее распространённые.
1. Scoped сервис создаётся только один раз для каждого веб-запроса
Разработчики могут выбрать время жизни сервиса при внедрении зависимости:
- Transient - новый экземпляр класса создается каждый раз, когда он запрашивается из контейнера DI,
- Singleton - один экземпляр класса будет создан на всё время существования приложения,
- Scoped - экземпляр класса должен быть создан один раз для каждого веб-запроса.
Область действия - это область кода между созданием экземпляра типа
Если один и тот же тип в области действия запрашивается несколько раз, всегда будет возвращаться один и тот же экземпляр. ASP.NET Core создаёт область действия в начале веб-запроса и удаляет её в конце веб-запроса. Поэтому экземпляры, ограниченные областью действия, создаются один раз для каждого веб-запроса. Но не всегда.
Ничто не мешает разработчикам создавать дочерние области действия. Экземпляры одного и того же типа, разрешённые из разных областей действия, также будут разными:
Продолжение следует…
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Начало
Разработчики, которые недавно начали работать с внедрением зависимостей в ASP.NET Core, часто неверно понимают принципы его работы. Некоторые заблуждения вызывают ошибки, а другие приводят к избыточному коду. Рассмотрим наиболее распространённые.
1. Scoped сервис создаётся только один раз для каждого веб-запроса
Разработчики могут выбрать время жизни сервиса при внедрении зависимости:
- Transient - новый экземпляр класса создается каждый раз, когда он запрашивается из контейнера DI,
- Singleton - один экземпляр класса будет создан на всё время существования приложения,
- Scoped - экземпляр класса должен быть создан один раз для каждого веб-запроса.
services.AddScoped<IUserRepository, UserRepository>();Однако время жизни, ограниченное областью действия, (scoped) не гарантирует, что класс создаётся только один раз для каждого веб-запроса. Это означает, что класс будет создан только один раз в заданной области действия, но в одном веб-запросе можно создать несколько областей.
Область действия - это область кода между созданием экземпляра типа
IServiceScope
и вызовом его метода Dispose. IServiceScope
содержит свойство ServiceProvider
, которое используется для разрешения экземпляров сервисов, зарегистрированных в методе ConfigureServices
.Если один и тот же тип в области действия запрашивается несколько раз, всегда будет возвращаться один и тот же экземпляр. ASP.NET Core создаёт область действия в начале веб-запроса и удаляет её в конце веб-запроса. Поэтому экземпляры, ограниченные областью действия, создаются один раз для каждого веб-запроса. Но не всегда.
Ничто не мешает разработчикам создавать дочерние области действия. Экземпляры одного и того же типа, разрешённые из разных областей действия, также будут разными:
public class SomeServiceБывают случаи, когда создание области вручную - это единственный правильный способ получить экземпляр сервиса, например, для разрешения scoped сервиса из синглтона. Знание особенностей областей действия может защитить вас от множества проблем.
{
private readonly IServiceScopeFactory _factory;
public SomeService(IServiceScopeFactory factory)
=> _factory = factory;
public void DoSomething()
{
using (IServiceScope scope = _factory.CreateScope());
var obj1 =
scope.ServiceProvider.GetService<IUserRepository>();
using (IServiceScope childScope =
_factory.CreateScope())
{
var obj2 = childScope.ServiceProvider
.GetService<IUserRepository>();
if (obj1 != obj2)
Console.WriteLine("Экземпляры разные.");
}
}
}
Продолжение следует…
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
День девятьсот восемьдесят восьмой. #ЗаметкиНаПолях
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Продолжение
Начало
2. DI фреймворк всегда проверяет захват зависимостей
Захват зависимостей - это своего рода антипаттерн внедрения зависимостей, который может привести к ошибкам в вашем коде.
Всё, что вам нужно сделать, чтобы получить проблему с захватом зависимости, - это внедрить класс с более коротким временем жизни в класс с более длительным временем жизни. Например, внедрите сервис, ограниченный областью действия, в синглтон. В этом случае класс, ограниченный областью действия, не будет создаваться и удаляться для каждого веб-запроса. Он будет иметь то же время жизни, что и синглтон, и фактически станет синглтоном.
Почему это действительно проблема?
В веб-приложениях экземпляры синглтонов, хранящие, например, кэшированные данные, спроектированы для обеспечения потоковой безопасности, чтобы избежать ошибок параллелизма, когда несколько запросов обращаются к одному и тому же синглтон объекту. Однако классы, ограниченные областью действия, не обязательно должны быть потокобезопасными, если разработчики не запускают дополнительные параллельные потоки в веб-запросе.
Проблема захвата зависимости продлевает время жизни объекта. Если потоконебезопасный класс, предназначенный для жизни внутри области действия, становится синглтоном, в приложении будут возникать ошибки параллелизма.
ASP.NET Core защищает разработчиков от этой проблемы, проверяя граф зависимостей на наличие захвата зависимостей и генерируя исключение, если они обнаружены.
Заблуждение состоит в том, что проверка ASP.NET Core на наличие захвата зависимостей всегда будет работать. Однако в ядре ASP.NET проверка зависимостей по умолчанию выполняется только в среде разработки (Development).
Если код не был хорошо протестирован локально, в других средах, кроме среды разработки, исключения не возникнет. Вместо этого время жизни сервисов будет без предупреждения увеличено, что вызовет потенциальные проблемы с параллелизмом и возможно другие.
Чтобы гарантировать, что код всегда проверяется на наличие захвата зависимостей, установите для параметра
Окончание следует…
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Продолжение
Начало
2. DI фреймворк всегда проверяет захват зависимостей
Захват зависимостей - это своего рода антипаттерн внедрения зависимостей, который может привести к ошибкам в вашем коде.
Всё, что вам нужно сделать, чтобы получить проблему с захватом зависимости, - это внедрить класс с более коротким временем жизни в класс с более длительным временем жизни. Например, внедрите сервис, ограниченный областью действия, в синглтон. В этом случае класс, ограниченный областью действия, не будет создаваться и удаляться для каждого веб-запроса. Он будет иметь то же время жизни, что и синглтон, и фактически станет синглтоном.
Почему это действительно проблема?
В веб-приложениях экземпляры синглтонов, хранящие, например, кэшированные данные, спроектированы для обеспечения потоковой безопасности, чтобы избежать ошибок параллелизма, когда несколько запросов обращаются к одному и тому же синглтон объекту. Однако классы, ограниченные областью действия, не обязательно должны быть потокобезопасными, если разработчики не запускают дополнительные параллельные потоки в веб-запросе.
Проблема захвата зависимости продлевает время жизни объекта. Если потоконебезопасный класс, предназначенный для жизни внутри области действия, становится синглтоном, в приложении будут возникать ошибки параллелизма.
ASP.NET Core защищает разработчиков от этой проблемы, проверяя граф зависимостей на наличие захвата зависимостей и генерируя исключение, если они обнаружены.
public void ConfigureServices(IServiceCollection services)Выполнение приведенного выше кода приведёт к исключению
{
services.AddSingleton<UserService>();
services.AddScoped<IUserRepository, UserRepository>();
}
public class UserService
{
public UserService(IUserRepository userRepository)
{
}
}
public class UserRepository : IUserRepository
{
}
InvalidOperationException
, из-за того, что класс, ограниченный областью действия, IUserRepository
не может быть использован из синглтона UserService
.Заблуждение состоит в том, что проверка ASP.NET Core на наличие захвата зависимостей всегда будет работать. Однако в ядре ASP.NET проверка зависимостей по умолчанию выполняется только в среде разработки (Development).
Если код не был хорошо протестирован локально, в других средах, кроме среды разработки, исключения не возникнет. Вместо этого время жизни сервисов будет без предупреждения увеличено, что вызовет потенциальные проблемы с параллелизмом и возможно другие.
Чтобы гарантировать, что код всегда проверяется на наличие захвата зависимостей, установите для параметра
ValidateScopes
значение true в точке входа приложения:public static IHostBuilder CreateHostBuilder(string[] args) =>Также вы можете указать свойство
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseDefaultServiceProvider((context, options) => {
options.ValidateScopes = true;
});
options.ValidateOnBuild = true;Эта функция добавлена в .NET Core 3.0. Проверка сервисов будет производиться во время построения провайдера сервисов, т.е. во время старта приложения, а не при запросе сервиса, который невозможно разрешить.
Окончание следует…
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
День девятьсот восемьдесят девятый. #ЗаметкиНаПолях
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Окончание
Начало
Продолжение
3. Контейнер DI всегда удаляет объекты
Контейнер DI отвечает не только за создание экземпляров объектов, но и за их удаление (dispose). Временные сервисы и сервисы, ограниченные областью действия, удаляются в конце области (в конце веб-запроса в ASP.NET Core), а классы-синглтоны удаляются перед завершением работы приложения. Контейнер DI полностью управляет временем жизни сервисов приложения. Разработчикам не нужно заботиться о вызове метода Dispose.
Однако в редких случаях разработчикам может потребоваться создать экземпляр класса самостоятельно, а затем предоставить экземпляр в контейнер DI:
4. Контейнер DI принимает только классы с интерфейсами
Внедрение зависимостей через конструктор довольно часто реализуется с использованием интерфейсов. Вот почему некоторые разработчики ошибочно полагают, что класс должен иметь интерфейс для регистрации в контейнере внедрения зависимостей. Это заблуждение приводит к извлечению интерфейса для классов, которым на самом деле интерфейс не нужен. В следующем примере показано, как зарегистрировать класс, не имеющий интерфейса, в контейнере DI:
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Окончание
Начало
Продолжение
3. Контейнер DI всегда удаляет объекты
Контейнер DI отвечает не только за создание экземпляров объектов, но и за их удаление (dispose). Временные сервисы и сервисы, ограниченные областью действия, удаляются в конце области (в конце веб-запроса в ASP.NET Core), а классы-синглтоны удаляются перед завершением работы приложения. Контейнер DI полностью управляет временем жизни сервисов приложения. Разработчикам не нужно заботиться о вызове метода Dispose.
Однако в редких случаях разработчикам может потребоваться создать экземпляр класса самостоятельно, а затем предоставить экземпляр в контейнер DI:
public void ConfigureServices(IServiceCollection services)В этом случае объект не будет удалён, как могли ожидать разработчики. Здесь им нужно не забыть удалить экземпляр самостоятельно, чтобы избежать проблем, которые могут возникнуть из-за не удалённых ресурсов. Разработчики могут просто ошибочно использовать приведённый выше синтаксис для регистрации объекта вместо следующего:
{
services.AddSingleton(new Service());
}
services.AddSingleton<Service>();В последнем случае объект будет создан, а затем удалён контейнером DI, что является лучшим вариантом. Оба варианта очень похожи, и легко случайно использовать не тот. Будьте осторожны.
4. Контейнер DI принимает только классы с интерфейсами
Внедрение зависимостей через конструктор довольно часто реализуется с использованием интерфейсов. Вот почему некоторые разработчики ошибочно полагают, что класс должен иметь интерфейс для регистрации в контейнере внедрения зависимостей. Это заблуждение приводит к извлечению интерфейса для классов, которым на самом деле интерфейс не нужен. В следующем примере показано, как зарегистрировать класс, не имеющий интерфейса, в контейнере DI:
public class SomeClassВнедрение класса в сервис:
{
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<SomeClass>();
}
public class SomeServiceИнтерфейсы - отличная функция, которая позволяет вам определить контракт для типа, добиться слабой связи, заменить типы во время выполнения, облегчить тестируемость и т.д. Однако в случаях, когда зависимость между классами приемлема, лучше не загрязнять код, создавая избыточные интерфейсы.
{
public SomeService(SomeClass someClass) {}
}
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
День девятьсот девяностый. #Оффтоп
Гугли Как Профессионал. 10 Советов по Эффективному Поиску
На старте карьеры многие программисты стесняются искать что-то в сети и в основном полагаются на свои скудные знания. Чем больше опыта мы приобретаем, тем больше понимаем, что запоминать детали синтаксиса каждого языка (а обычно за несколько лет карьеры приходится таки попробовать разные языки) или алгоритмы разворачивания деревьев – это пустая трата времени и «памяти». Поэтому всё большую ценность приобретает умение быстро найти решение в гугле.
1. Точный поиск
Начнём с простого. Поискать полный текст, а не по каждому слову отдельно (например, полный текст ошибки, которую выдал компилятор), можно, заключив текст в кавычки. В противном случае гугл включит в результаты поиск по отдельным словам и синонимам.
2. Поиск на определённом сайте
Добавьте в строку поиска параметр
Добавьте ключевое слово со знаком минус. Например:
4. Новые/старые результаты
Чтобы получать результаты до/после определённого года, можно добавить параметры
5. Любой интервал
Параметры before и after можно использовать совместно, и задать интервал, например,
6. Выбор варианта
Можно использовать знак
Может показаться, что использование подстановки для «чего угодно» - бессмысленно, однако это позволяет, например, найти все поддомены у сайта, кроме www:
Тоже довольно широко известный параметр. Добавьте
Используйте параметр
10. Поиск в кэше
Параметр
Источник: https://youtu.be/cEBkvm0-rg0
Гугли Как Профессионал. 10 Советов по Эффективному Поиску
На старте карьеры многие программисты стесняются искать что-то в сети и в основном полагаются на свои скудные знания. Чем больше опыта мы приобретаем, тем больше понимаем, что запоминать детали синтаксиса каждого языка (а обычно за несколько лет карьеры приходится таки попробовать разные языки) или алгоритмы разворачивания деревьев – это пустая трата времени и «памяти». Поэтому всё большую ценность приобретает умение быстро найти решение в гугле.
1. Точный поиск
Начнём с простого. Поискать полный текст, а не по каждому слову отдельно (например, полный текст ошибки, которую выдал компилятор), можно, заключив текст в кавычки. В противном случае гугл включит в результаты поиск по отдельным словам и синонимам.
2. Поиск на определённом сайте
Добавьте в строку поиска параметр
site:URL
, например: nullreferenceexception site:stackoverflow.com
3. Исключить из результатовДобавьте ключевое слово со знаком минус. Например:
get dom element -jqueryК примеру, так можно исключить имя вашего класса из полного текста ошибки.
4. Новые/старые результаты
Чтобы получать результаты до/после определённого года, можно добавить параметры
after:2005
или before:2015
. Полезно, чтобы искать, например, документацию по .NET Framework.5. Любой интервал
Параметры before и after можно использовать совместно, и задать интервал, например,
2013..2019
. Хотя, интервал в принципе может быть любым, вроде $1000..$300000
.6. Выбор варианта
Можно использовать знак
|
для выбора между вариантами. Также, если кроме этих вариантов в запросе есть другие слова, можно заключить выбор в скобки, как в выражении regex:(A|B) C7. Wildcard
Может показаться, что использование подстановки для «чего угодно» - бессмысленно, однако это позволяет, например, найти все поддомены у сайта, кроме www:
site:*.microsoft.com -www8. Тип файла
Тоже довольно широко известный параметр. Добавьте
filetype
к запросу, чтобы искать только определённый тип файлов, например: filetype:pdf
9. Аналогичные сайтыИспользуйте параметр
related:URL
, чтобы найти сайты, аналогичные заданному. Например: related:google.com
позволит найти альтернативные поисковики.10. Поиск в кэше
Параметр
cache:url
позволяет поискать сайт в кэше. Например, узнать, когда страница в последний раз была кэширована гуглом.Источник: https://youtu.be/cEBkvm0-rg0
👍3
День девятьсот девяносто первый. #ЗаметкиНаПолях #AsyncTips
Ожидание завершения группы задач
Задача: есть несколько задач, и нужно подождать, пока они все завершатся.
Решение
Для этой цели существует метод
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
Ожидание завершения группы задач
Задача: есть несколько задач, и нужно подождать, пока они все завершатся.
Решение
Для этой цели существует метод
Task.WhenAll
. Он получает несколько задач и возвращает задачу, которая завершается при завершении всех указанных задач.Task task1 = Task.Delay(100);Если все задачи имеют одинаковый тип результата и все завершаются успешно, то задача
Task task2 = Task.Delay(200);
Task task3 = Task.Delay(150);
await Task.WhenAll(task1, task2, task3);
Task.WhenAll
возвращает массив, содержащий результаты всех задач:Task<int> task1 = Task.FromResult(3);Есть перегруженная версия
Task<int> task2 = Task.FromResult(5);
Task<int> task3 = Task.FromResult(7);
int[] results = await Task.WhenAll(task1, task2, task3);
// "results" содержит { 3, 5, 7 }
Task.WhenAll
, которая получает IEnumerable
с задачами. Тем не менее заметьте, что при использовании LINQ код получается более понятным, когда последовательность явно материализуется:async Task<string> GetAllAsync(Если одна из задач выбросит исключение, то
HttpClient client,
IEnumerable<string> urls)
{
// Определяем действие для каждого URL
var downloads = urls.Select(url => client.GetStringAsync(url));
// Задачи ещё не запущены, т.к.
// последовательность не материализована
// Запускаем загрузку для всех URL одновременно
Task<string>[] downloadTasks = downloads.ToArray();
// Все задачи запущены.
// Асинхронно ожидаем завершения всех загрузок
string[] htmlPages =
await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}
Task.WhenAll
вернёт задачу в статусе Faulted
с этим исключением. Если сразу несколько задач выдают исключения, то все они помещаются в задачу, возвращаемую Task.WhenAll
. При ожидании этой задачи будет выдано только одно из них:tryОбычно первого «попавшегося» исключения достаточно, но, если вам нужны все исключения, проверьте свойство
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex)
{
// "ex" – исключение либо из task1, либо из task2
...
}
Exception
задачи, возвращаемой Task.WhenAll
:Task allTasks = Task.WhenAll(task1, task2);Если ни одна задача не выбросила исключения, но хотя бы одна задача была отменена, то задача, возвращённая
try
{
await Task.WhenAll(allTasks);
}
catch
{
AggregateException allExceptions = allTasks.Exception;
...
}
Task.WhenAll
, будет в статусе Canceled
.Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
👍1
День девятьсот девяносто второй. #ЗаметкиНаПолях #AsyncTips
Отмена Группы Задач
Вчерашний пост вызвал в чате обсуждение. Возник такой вопрос: если одна из ожидаемых задач выбросит исключение, можно ли отменить остальные? Уточню вводные:
- задачи формально не связаны, но результат нужен или их всех, или никакого;
- задачи нужно выполнять параллельно.
Первой моей идеей было передать во все задачи один токен отмены, и в случае исключения отменять все задачи одним токеном. Проблема в том, что сам токен не позволяет инициировать отмену (он только реагирует на отмену), поэтому в задачи нужно передавать
Итак, у нас есть «хорошие» и «плохие» задачи (передаём в них время задержки delay для удобства тестирования):
Далее создаём несколько задач, «оборачивая» их в наш метод
Запускаем:
Источник: https://stackoverflow.com/questions/19394150/tpl-execute-dependent-task-and-stop-all-task-when-some-task-fails#19397190
Отмена Группы Задач
Вчерашний пост вызвал в чате обсуждение. Возник такой вопрос: если одна из ожидаемых задач выбросит исключение, можно ли отменить остальные? Уточню вводные:
- задачи формально не связаны, но результат нужен или их всех, или никакого;
- задачи нужно выполнять параллельно.
Первой моей идеей было передать во все задачи один токен отмены, и в случае исключения отменять все задачи одним токеном. Проблема в том, что сам токен не позволяет инициировать отмену (он только реагирует на отмену), поэтому в задачи нужно передавать
CancellationTokenSource
. В итоге, поискав решение, я нашёл интересный пост на StackOverflow, кратким описанием которого хочу поделиться. Извините за однобуквенные имена, сокращал, как мог. Полный код по ссылке ниже.Итак, у нас есть «хорошие» и «плохие» задачи (передаём в них время задержки delay для удобства тестирования):
async Task<object> DoAsync(string id,Следующий метод «оборачивает» задачу, перехватывая исключения в ней и вызывая отмену остальных:
CancellationToken token, int delay)
{
await Task.Delay(delay, token);
return id;
}
async Task<object> BadAsync(string id,
CancellationToken token, int delay)
{
await Task.Delay(delay, token);
throw new Exception(id);
}
async Task<T> Wrap<T>(Task<T> task,Теперь собственно метод, выполняющий все задачи:
CancellationTokenSource cts)
{
try {
return await task;
}
catch {
if (!cts.IsCancellationRequested)
cts.Cancel(); // отменяем остальные
throw;
}
}
public async Task DoWorkAsync(CancellationToken t)Здесь мы используем внешний токен отмены
{
var tasks = new List<Task<object>>();
var cts = new CancellationTokenSource();
var ct = cts.Token;
try {
using (t.Register(() => cts.Cancel())) {
// параллельно запускаем задачи
tasks.Add(Wrap(DoAsync("1", ct, 500), cts));
tasks.Add(Wrap(DoAsync("2", ct, 1000), cts));
tasks.Add(Wrap(DoAsync("3", ct, 1500), cts));
tasks.Add(Wrap(BadAsync("Bad", ct, 700), cts));
await Task.WhenAll(tasks.ToArray());
}
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
Console.WriteLine("Results:");
tasks.ForEach((task) =>
Console.WriteLine(task.Status));
}
t
, чтобы иметь возможность отменить все задачи извне. В директиве using регистрируем действие, которое будет выполнено при отмене внешнего токена.Далее создаём несколько задач, «оборачивая» их в наш метод
Wrap
. Передаём в них один токен отмены и разные времена задержки, имитируя асинхронную работу. Наша «плохая» задача выбросит исключение после задачи 1, но перед завершением задач 2 и 3, метод Wrap
перехватит исключение и отменит задачи 2 и 3.Запускаем:
var cts = new CancellationTokenSource(10000);Подробный код и обработка различных вариантов завершения группы задач по ссылке ниже.
DoWorkAsync(cts.Token).Wait();
//Вывод (в порядке регистрации задач):
RanToCompletion
Canceled
Canceled
Faulted
Источник: https://stackoverflow.com/questions/19394150/tpl-execute-dependent-task-and-stop-all-task-when-some-task-fails#19397190
👍2
День девятьсот девяносто третий. #Testing
7 Принципов Тестирования ПО
Опять про тестирование? Да. На этот раз глазами QA. Однако сегодня рассмотрим не советы о том, как тестировать, а общие принципы тестирования. То, чего следует и не следует ожидать при написании тестов.
1. Тестирование показывает наличие дефектов, а не их отсутствие
Многие тестировщики могли попадать в такую ситуацию: владелец продукта или даже клиент говорят, что ваша задача - гарантировать, что в результатах спринта нет ошибок.
Тестирование - это деструктивное действие, его цель - вывести программу из строя. Когда тестировщик пишет тестовый пример, он предназначен для поиска ошибки, а не для доказательства её отсутствия.
Итак, этот принцип гласит, что невозможно гарантировать отсутствие дефектов в объекте тестирования. Этот принцип не предназначен для оправдания недостаточно качественного тестирования и пропущенных ошибок. Он говорит о том, что нужно избегать ложных ожиданий в отношении продукта и результатов тестирования.
2. Исчерпывающее тестирование невозможно
У вас, вероятно, никогда не будет времени протестировать все возможные сценарии в продукте. Представьте себе небольшую форму с 3 полями выбора, каждое с 10 вариантами. Чтобы проверить все возможные сценарии, вам потребуется 1000 тестов.
Мы можем использовать метод тестирования, называемый эквивалентным разбиением. Вкратце, он состоит из разделения тестовых данных на блоки таким образом, что ожидается, что все элементы одного блока будут обрабатываться одинаково.
А при острой нехватке времени на тесты рекомендуется провести анализ рисков и расставить приоритеты, какие тесты писать в первую очередь с учётом более высоких рисков для бизнеса.
3. Раннее тестирование экономит время и деньги
Работа по тестированию должна начинаться как можно раньше в проекте. Действия по тестированию не ограничиваются выполнением или автоматизацией end-to-end тестов, когда функционал готов. Тестировщик также может просматривать документацию и требования, чтобы не допустить перехода ошибок на более поздние этапы жизненного цикла разработки. Помните, что чем позже вы обнаружите проблему, тем больше будет стоить её устранение.
4. Дефекты объединяются
Этот принцип является применением закона Парето: для многих исходов примерно 80% последствий происходят от 20% причин.
При тестировании ПО высока вероятность того, что 80% проблем можно найти в 20% модулей/компонентов. Так что будьте осторожны, когда найдёте ошибку, потому что в той же функции могут скрываться и другие.
5. Остерегайтесь парадокса пестицидов
Многократное использование одной и той же смеси пестицидов для уничтожения насекомых со временем приведет к развитию у насекомых устойчивости к пестициду.
Если вы используете один и тот же приём тестирования снова и снова, скорее всего, он станет менее эффективным. Рассмотрите другой способ, который имеет более высокую вероятность обнаружения ошибок.
6. Тестирование зависит от контекста
Тестирование в agile проекте выполняется иначе, чем в waterfall проекте, из-за различий в периодичности выпуска.
Тестирование электронной коммерции проводится иначе, чем тестирование системы интернет-банкинга, из-за различных рисков.
Правильный инструмент для автоматизации регрессионных тестов также сильно зависит от контекста. По мере того, как вы набираетесь опыта, вы лучше понимаете контекст и лучше планируете работу по обеспечению качества, чтобы она соответствовала контексту.
7. Отсутствие ошибок - это заблуждение
Из второго принципа мы знаем, что тестирование показывает наличие дефектов, а не их отсутствие. Этот принцип гласит, что независимо от того, сколько усилий вы или ваша команда прилагаете к тестированию, сами по себе тесты никогда не могут гарантировать безошибочность ПО.
Считается, что ПО содержит ошибку только тогда, когда что-то работает не так, как ожидалось. Однако система, которая слишком сложна для пользователя (ошибки в удобстве использования) или не полностью соответствует потребностям пользователя (ошибки в моделировании), также считается имеющей ошибки.
Источник
7 Принципов Тестирования ПО
Опять про тестирование? Да. На этот раз глазами QA. Однако сегодня рассмотрим не советы о том, как тестировать, а общие принципы тестирования. То, чего следует и не следует ожидать при написании тестов.
1. Тестирование показывает наличие дефектов, а не их отсутствие
Многие тестировщики могли попадать в такую ситуацию: владелец продукта или даже клиент говорят, что ваша задача - гарантировать, что в результатах спринта нет ошибок.
Тестирование - это деструктивное действие, его цель - вывести программу из строя. Когда тестировщик пишет тестовый пример, он предназначен для поиска ошибки, а не для доказательства её отсутствия.
Итак, этот принцип гласит, что невозможно гарантировать отсутствие дефектов в объекте тестирования. Этот принцип не предназначен для оправдания недостаточно качественного тестирования и пропущенных ошибок. Он говорит о том, что нужно избегать ложных ожиданий в отношении продукта и результатов тестирования.
2. Исчерпывающее тестирование невозможно
У вас, вероятно, никогда не будет времени протестировать все возможные сценарии в продукте. Представьте себе небольшую форму с 3 полями выбора, каждое с 10 вариантами. Чтобы проверить все возможные сценарии, вам потребуется 1000 тестов.
Мы можем использовать метод тестирования, называемый эквивалентным разбиением. Вкратце, он состоит из разделения тестовых данных на блоки таким образом, что ожидается, что все элементы одного блока будут обрабатываться одинаково.
А при острой нехватке времени на тесты рекомендуется провести анализ рисков и расставить приоритеты, какие тесты писать в первую очередь с учётом более высоких рисков для бизнеса.
3. Раннее тестирование экономит время и деньги
Работа по тестированию должна начинаться как можно раньше в проекте. Действия по тестированию не ограничиваются выполнением или автоматизацией end-to-end тестов, когда функционал готов. Тестировщик также может просматривать документацию и требования, чтобы не допустить перехода ошибок на более поздние этапы жизненного цикла разработки. Помните, что чем позже вы обнаружите проблему, тем больше будет стоить её устранение.
4. Дефекты объединяются
Этот принцип является применением закона Парето: для многих исходов примерно 80% последствий происходят от 20% причин.
При тестировании ПО высока вероятность того, что 80% проблем можно найти в 20% модулей/компонентов. Так что будьте осторожны, когда найдёте ошибку, потому что в той же функции могут скрываться и другие.
5. Остерегайтесь парадокса пестицидов
Многократное использование одной и той же смеси пестицидов для уничтожения насекомых со временем приведет к развитию у насекомых устойчивости к пестициду.
Если вы используете один и тот же приём тестирования снова и снова, скорее всего, он станет менее эффективным. Рассмотрите другой способ, который имеет более высокую вероятность обнаружения ошибок.
6. Тестирование зависит от контекста
Тестирование в agile проекте выполняется иначе, чем в waterfall проекте, из-за различий в периодичности выпуска.
Тестирование электронной коммерции проводится иначе, чем тестирование системы интернет-банкинга, из-за различных рисков.
Правильный инструмент для автоматизации регрессионных тестов также сильно зависит от контекста. По мере того, как вы набираетесь опыта, вы лучше понимаете контекст и лучше планируете работу по обеспечению качества, чтобы она соответствовала контексту.
7. Отсутствие ошибок - это заблуждение
Из второго принципа мы знаем, что тестирование показывает наличие дефектов, а не их отсутствие. Этот принцип гласит, что независимо от того, сколько усилий вы или ваша команда прилагаете к тестированию, сами по себе тесты никогда не могут гарантировать безошибочность ПО.
Считается, что ПО содержит ошибку только тогда, когда что-то работает не так, как ожидалось. Однако система, которая слишком сложна для пользователя (ошибки в удобстве использования) или не полностью соответствует потребностям пользователя (ошибки в моделировании), также считается имеющей ошибки.
Источник
День девятьсот девяносто четвёртый. #ЗаметкиНаПолях
Сравнение Строк Сложнее, Чем Кажется. Начало
Сравнение строк отличается от сравнения чисел. 2 числа равны, если их значения идентичны. Для строк всё сложнее. Например, вы хотите сравнение с учетом регистра? А как насчёт разных способов написать одну и ту же букву? Например, в немецком языке часто встречается буква
В .NET есть 6 режимов сравнения строк:
1. Ordinal: выполняет простое сравнение байтов независимо от языка. Наиболее удобен для сравнения ресурсов, чувствительных к регистру, таких как пароли.
2. OrdinalIgnoreCase: обрабатывает символы в строках для сравнения, как если бы они были преобразованы в верхний регистр с использованием соглашений инвариантного языка и региональных параметров, а затем выполняет простое сравнение байтов, которое не зависит от языка. Подходит для сравнения строк, созданных программно, когда регистр не важен, таких как пути и имена файлов.
3. InvariantCulture: сравнивает строки лингвистически релевантным способом, но он не подходит для отображения в какой-либо конкретной культуре. Основное применение - упорядочивание строк таким образом, чтобы оно было идентичным для разных культур.
4. InvariantCultureIgnoreCase: сравнивает строки лингвистически релевантным способом, игнорируя регистр.
5. CurrentCulture: может использоваться, когда строки имеют лингвистическое значение (например, отображаются для пользователя или являются результатом взаимодействия с пользователем).
6. CurrentCultureIgnoreCase: может использоваться, когда строки являются лингвистически релевантными, а их регистр - нет.
Важно явно указывать режим сравнения, чтобы избежать неожиданного поведения или, что еще хуже, проблем с безопасностью. Например, если вы сравните 2 пароля, используя текущую культуру или инвариантную культуру, пароли могут быть одинаковыми, тогда как на самом деле они могут быть разными!
Примеры
Понять разницу между Ordinal и OrdinalIgnoreCase довольно просто. Сложнее понять культурно-зависимые сравнения.
Примечание: В примерах ниже последним параметром идёт перечисление
Источник: https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm
Сравнение Строк Сложнее, Чем Кажется. Начало
Сравнение строк отличается от сравнения чисел. 2 числа равны, если их значения идентичны. Для строк всё сложнее. Например, вы хотите сравнение с учетом регистра? А как насчёт разных способов написать одну и ту же букву? Например, в немецком языке часто встречается буква
ß
, но её также можно написать ss
, так как это проще на многих клавиатурах.В .NET есть 6 режимов сравнения строк:
1. Ordinal: выполняет простое сравнение байтов независимо от языка. Наиболее удобен для сравнения ресурсов, чувствительных к регистру, таких как пароли.
2. OrdinalIgnoreCase: обрабатывает символы в строках для сравнения, как если бы они были преобразованы в верхний регистр с использованием соглашений инвариантного языка и региональных параметров, а затем выполняет простое сравнение байтов, которое не зависит от языка. Подходит для сравнения строк, созданных программно, когда регистр не важен, таких как пути и имена файлов.
3. InvariantCulture: сравнивает строки лингвистически релевантным способом, но он не подходит для отображения в какой-либо конкретной культуре. Основное применение - упорядочивание строк таким образом, чтобы оно было идентичным для разных культур.
4. InvariantCultureIgnoreCase: сравнивает строки лингвистически релевантным способом, игнорируя регистр.
5. CurrentCulture: может использоваться, когда строки имеют лингвистическое значение (например, отображаются для пользователя или являются результатом взаимодействия с пользователем).
6. CurrentCultureIgnoreCase: может использоваться, когда строки являются лингвистически релевантными, а их регистр - нет.
Важно явно указывать режим сравнения, чтобы избежать неожиданного поведения или, что еще хуже, проблем с безопасностью. Например, если вы сравните 2 пароля, используя текущую культуру или инвариантную культуру, пароли могут быть одинаковыми, тогда как на самом деле они могут быть разными!
Примеры
Понять разницу между Ordinal и OrdinalIgnoreCase довольно просто. Сложнее понять культурно-зависимые сравнения.
Примечание: В примерах ниже последним параметром идёт перечисление
StringComparison
. Для краткости я оставил лишь значение перечисления, поэтому подразумевается, что везде используется using static System.StringComparison;
// false
string.Equals("ss", "ß", OrdinalIgnoreCase);
// true в Windows / false в Linux (WSL) – об этом позже
string.Equals("ss", "ß", InvariantCulture);
\0
и ещё некоторые символы игнорируются в лингвистических сравнениях:// falseСравнение с учетом культуры сравнивает графемы. Например, строка
string.Equals("a\0b", "ab", Ordinal);
// true
string.Equals("a\0b", "ab", InvariantCulture);
// true
string.Equals("a\0b", "ab", CurrentCulture);
A\r\nB
разделена на A
, \r\n
и B
. Это означает, что \n
не является частью этой строки. Но \r
или \r\n
являются частью строки:// trueПродолжение следует…
"A\r\nB".Contains("\n", Ordinal);
// false
"A\r\nB".Contains("\n", InvariantCulture);
//true
"A\r\nB".Contains("\r", InvariantCulture);
Источник: https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm
👍1
День девятьсот девяносто пятый. #ЗаметкиНаПолях
Сравнение Строк Сложнее, Чем Кажется. Продолжение
Начало
NLS и ICU
В прошлом API глобализации .NET использовали разные базовые библиотеки на разных платформах. В Unix использовались международные компоненты для Unicode (ICU), а в Windows использовалась поддержка национальных языков (NLS). Это привело к некоторым различиям в поведении нескольких API глобализации при запуске приложений на разных платформах. В .NET 5.0 поведение среды выполнения изменилось. API глобализации теперь по умолчанию используют ICU на всех поддерживаемых платформах. Это позволяет приложениям избегать различий между платформами.
Обратите внимание, что ICU также используется в Windows. DLL-библиотеки ICU поставляются с Windows, начиная с Windows 10 версии 1703. Многие приложения уже начали использовать ICU в Windows. Таким образом, переход на использование ICU происходит повсюду, а не только в .NET.
.NET Framework 1.0-4.8: NLS в Windows
NET Core 1.0-3.1: NLS в Windows, ICU в Linux
.NET 5.0+: ICU* в Windows, ICU в Linux
* Можно принудительно использовать NLS с помощью флага System.Globalization.UseNls в файле конфигурации.
Если в версии Windows нет ICU, она автоматически возвращается к NLS. NLS и ICU в некоторых крайних случаях ведут себя по-разному (см. ниже). Кроме того, новая версия библиотеки может вносить изменения в поведение.
Обратите внимание, что переход с NLS на ICU не является критическим изменением. Изменения данных глобализации происходят даже в NLS, и поведение API не следует считать стабильным.
У методов разные режимы сравнения по умолчанию
Вы всегда должны явно указывать компаратор, поскольку значения по умолчанию в разных методах не согласованы. Например,
Анализаторы кода могут обнаруживать места потенциальных ошибок. Чтобы защититься от неожиданного поведения, рекомендуется включить в проекте анализаторы кода Roslyn. Они помогут пометить код, который может непреднамеренно использовать лингвистический компаратор, когда вероятнее всего подразумевался порядковый компаратор. Следующие правила должны помочь выявить эти проблемы:
- CA1307: Specify StringComparison for clarity (для ясности укажите StringComparison)
- CA1309: Use ordinal StringComparison (используйте порядковый режим сравнения)
- CA1310: Specify StringComparison for correctness (укажите StringComparison для корректности)
Эти особые правила не включены по умолчанию. Чтобы включить их и отображать любые нарушения как ошибки сборки, установите следующие свойства в файле проекта:
- https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm
- https://docs.microsoft.com/en-us/dotnet/standard/base-types/string-comparison-net-5-plus
Сравнение Строк Сложнее, Чем Кажется. Продолжение
Начало
NLS и ICU
В прошлом API глобализации .NET использовали разные базовые библиотеки на разных платформах. В Unix использовались международные компоненты для Unicode (ICU), а в Windows использовалась поддержка национальных языков (NLS). Это привело к некоторым различиям в поведении нескольких API глобализации при запуске приложений на разных платформах. В .NET 5.0 поведение среды выполнения изменилось. API глобализации теперь по умолчанию используют ICU на всех поддерживаемых платформах. Это позволяет приложениям избегать различий между платформами.
Обратите внимание, что ICU также используется в Windows. DLL-библиотеки ICU поставляются с Windows, начиная с Windows 10 версии 1703. Многие приложения уже начали использовать ICU в Windows. Таким образом, переход на использование ICU происходит повсюду, а не только в .NET.
.NET Framework 1.0-4.8: NLS в Windows
NET Core 1.0-3.1: NLS в Windows, ICU в Linux
.NET 5.0+: ICU* в Windows, ICU в Linux
* Можно принудительно использовать NLS с помощью флага System.Globalization.UseNls в файле конфигурации.
Если в версии Windows нет ICU, она автоматически возвращается к NLS. NLS и ICU в некоторых крайних случаях ведут себя по-разному (см. ниже). Кроме того, новая версия библиотеки может вносить изменения в поведение.
Обратите внимание, что переход с NLS на ICU не является критическим изменением. Изменения данных глобализации происходят даже в NLS, и поведение API не следует считать стабильным.
У методов разные режимы сравнения по умолчанию
Вы всегда должны явно указывать компаратор, поскольку значения по умолчанию в разных методах не согласованы. Например,
string.IndexOf
использует текущий язык и региональные параметры, тогда как string.Equals
использует Ordinal
. Поэтому всегда используйте перегрузку метода с StringComparison
, IEqualityComparer<string>
или IComparer<string>
, когда это возможно:// true, т.к. используется режим сравнения Ordinal
"A\r\nB".Contains("\n");
IndexOf
по причинам, описанным выше, в .NET 5+ выдаёт -1, т.к. используется текущая культура, а до .NET 5 в результате будет 2:"A\r\nB".IndexOf("\n");Это также относится к
Equals
и CompareTo
(и всем операторам ==
, <
, >
, и т.п.):// falseA
"encyclopædia".Equals("encyclopaedia");
CompareTo
опять же выдаёт 0 (равны) до .NET 5 и 1 в .NET 5+:"encyclopædia".CompareTo("encyclopaedia");Защита от неожиданного поведения
Анализаторы кода могут обнаруживать места потенциальных ошибок. Чтобы защититься от неожиданного поведения, рекомендуется включить в проекте анализаторы кода Roslyn. Они помогут пометить код, который может непреднамеренно использовать лингвистический компаратор, когда вероятнее всего подразумевался порядковый компаратор. Следующие правила должны помочь выявить эти проблемы:
- CA1307: Specify StringComparison for clarity (для ясности укажите StringComparison)
- CA1309: Use ordinal StringComparison (используйте порядковый режим сравнения)
- CA1310: Specify StringComparison for correctness (укажите StringComparison для корректности)
Эти особые правила не включены по умолчанию. Чтобы включить их и отображать любые нарушения как ошибки сборки, установите следующие свойства в файле проекта:
<PropertyGroup>Источники:
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<WarningsAsErrors>
$(WarningsAsErrors);CA1307;CA1309;CA1310
</WarningsAsErrors>
</PropertyGroup>
- https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm
- https://docs.microsoft.com/en-us/dotnet/standard/base-types/string-comparison-net-5-plus
День девятьсот девяносто шестой. #Оффтоп
Противоречивые Мнения Разработчиков ПО
Отличной пятницы вам, господа. И у меня для вас сегодня вот такая тема для обсуждения. Периодически мы сталкиваемся с такими рассуждениями из уст даже уважаемых в сообществе людей. Приведу несколько примеров из видео по ссылке ниже (кроме тех, которые на канале и так широко обсуждались), а в комментариях расскажите, с чем согласны, с чем нет. И предлагайте свои варианты, от которых у вас «пригорает» больше всего.
1. Программисты, которые не пишут код в свободное время для развлечения, никогда не станут такими же хорошими, как те, кто это делает
Даже самые умные и талантливые люди никогда не станут по-настоящему хорошими программистами, если они не будут относиться к этому как к чему-то большему, чем работа. То есть они они делают небольшие проекты на стороне или просто возятся с множеством разных языков и идей в свободное время. Это не значит, что хорошие программисты ничего не делают, кроме программирования, но они делают больше, чем просто пишут код с 9 до 5.
2. Ленивые программисты - лучшие программисты
Ленивый программист чаще находит способы сократить время, затрачиваемое на написание кода. Это в итоге превращается в инструменты и рабочие процессы, которые могут принести пользу другим разработчикам в компании/команде. Такие разработчики могут искать утилиты или писать мелкие скрипты для автоматизации часто выполняемых действий. Ленивому программисту может потребоваться несколько дополнительных часов, чтобы выпустить первую версию продукта, но он сэкономит месяцы в будущем.
3. ИТ специальность делает вас более разносторонним программистом
Формальное образование открывает вам другие возможности мышления и даёт отличный базис. То, что вы написали код лучше, чем бакалавр в ИТ, не значит, что вы лучше него. Он сможет легко наверстать упущенное, а вот наоборот – вряд ли. Наличие квалификации также показывает целеустремлённость, настойчивость и тот факт, что человек сделает всё возможное, чтобы стать лучше.
4. Тесты не нужно писать заранее, а иногда и вовсе не нужно
Разработчики плохо тестируют собственный код. Вот почему обычно есть группы QA. В большинстве случаев код, который мы пишем, переплетается с другим кодом, который нужно тестировать отдельно, поэтому мы в итоге обмазываемся паттернами, чтобы обеспечить тестируемость. Не то чтобы паттерны – это плохо, но иногда они могут добавлять ненужной сложности, и все ради тестирования… что часто всё равно не работает. Чтобы написать исчерпывающий тест, требуется много времени. Часто больше времени, чем мы готовы выделить. И чем более комплексным является тест, тем более хрупким он становится. Если интерфейс объекта, который он тестирует, изменяется, мы вынуждены переписать тест, который больше не срабатывает. А если меняются требования к системе…
5. Клиент не всегда прав
Слишком часто разработчики просто пишут код, который их просят, и не пытаются разобраться в продукте. Роль разработчика в том, чтобы помогать бизнесу выражать свои идеи. При этом разработчик должен быть заинтересован в понимании бизнеса, чтобы он мог предоставить наилучший возможный результат. И это подразумевает, что иногда владелец продукта будет просить сделать «революционную новинку», а разработчику придётся либо согласиться с этим фактом, либо объяснять наиболее вероятную причину, почему это невозможно. Это взаимовыгодно, потому что владелец продукта понимает, как его мысли воплощаются в продукте, а команда разработчиков понимает, что они делают нечто большее, чем просто пишут код.
6. Не страшно чего-то не знать. Но тебя уволят, если ты даже не сможешь это нагуглить
Интернет (и гугл в частности) - это инструмент. И эффективное его использование – это один из важных навыков, которым должен обладать разработчик.
Источник: https://youtu.be/goy4lZfDtCE
Противоречивые Мнения Разработчиков ПО
Отличной пятницы вам, господа. И у меня для вас сегодня вот такая тема для обсуждения. Периодически мы сталкиваемся с такими рассуждениями из уст даже уважаемых в сообществе людей. Приведу несколько примеров из видео по ссылке ниже (кроме тех, которые на канале и так широко обсуждались), а в комментариях расскажите, с чем согласны, с чем нет. И предлагайте свои варианты, от которых у вас «пригорает» больше всего.
1. Программисты, которые не пишут код в свободное время для развлечения, никогда не станут такими же хорошими, как те, кто это делает
Даже самые умные и талантливые люди никогда не станут по-настоящему хорошими программистами, если они не будут относиться к этому как к чему-то большему, чем работа. То есть они они делают небольшие проекты на стороне или просто возятся с множеством разных языков и идей в свободное время. Это не значит, что хорошие программисты ничего не делают, кроме программирования, но они делают больше, чем просто пишут код с 9 до 5.
2. Ленивые программисты - лучшие программисты
Ленивый программист чаще находит способы сократить время, затрачиваемое на написание кода. Это в итоге превращается в инструменты и рабочие процессы, которые могут принести пользу другим разработчикам в компании/команде. Такие разработчики могут искать утилиты или писать мелкие скрипты для автоматизации часто выполняемых действий. Ленивому программисту может потребоваться несколько дополнительных часов, чтобы выпустить первую версию продукта, но он сэкономит месяцы в будущем.
3. ИТ специальность делает вас более разносторонним программистом
Формальное образование открывает вам другие возможности мышления и даёт отличный базис. То, что вы написали код лучше, чем бакалавр в ИТ, не значит, что вы лучше него. Он сможет легко наверстать упущенное, а вот наоборот – вряд ли. Наличие квалификации также показывает целеустремлённость, настойчивость и тот факт, что человек сделает всё возможное, чтобы стать лучше.
4. Тесты не нужно писать заранее, а иногда и вовсе не нужно
Разработчики плохо тестируют собственный код. Вот почему обычно есть группы QA. В большинстве случаев код, который мы пишем, переплетается с другим кодом, который нужно тестировать отдельно, поэтому мы в итоге обмазываемся паттернами, чтобы обеспечить тестируемость. Не то чтобы паттерны – это плохо, но иногда они могут добавлять ненужной сложности, и все ради тестирования… что часто всё равно не работает. Чтобы написать исчерпывающий тест, требуется много времени. Часто больше времени, чем мы готовы выделить. И чем более комплексным является тест, тем более хрупким он становится. Если интерфейс объекта, который он тестирует, изменяется, мы вынуждены переписать тест, который больше не срабатывает. А если меняются требования к системе…
5. Клиент не всегда прав
Слишком часто разработчики просто пишут код, который их просят, и не пытаются разобраться в продукте. Роль разработчика в том, чтобы помогать бизнесу выражать свои идеи. При этом разработчик должен быть заинтересован в понимании бизнеса, чтобы он мог предоставить наилучший возможный результат. И это подразумевает, что иногда владелец продукта будет просить сделать «революционную новинку», а разработчику придётся либо согласиться с этим фактом, либо объяснять наиболее вероятную причину, почему это невозможно. Это взаимовыгодно, потому что владелец продукта понимает, как его мысли воплощаются в продукте, а команда разработчиков понимает, что они делают нечто большее, чем просто пишут код.
6. Не страшно чего-то не знать. Но тебя уволят, если ты даже не сможешь это нагуглить
Интернет (и гугл в частности) - это инструмент. И эффективное его использование – это один из важных навыков, которым должен обладать разработчик.
Источник: https://youtu.be/goy4lZfDtCE
👍1
День девятьсот девяносто седьмой. #ПолезныйКод
Загрузка сертификата SSL/TLS в .NET
Недавно мне потребовалось настроить оповещение, когда срок действия сертификата подходит к концу или когда он использует небезопасный алгоритм подписи. Первый шаг - получить сертификат. В .NET для этого вы можете использовать класс
Источник: https://www.meziantou.net/downloading-a-ssl-certificate-in-dotnet.htm
Загрузка сертификата SSL/TLS в .NET
Недавно мне потребовалось настроить оповещение, когда срок действия сертификата подходит к концу или когда он использует небезопасный алгоритм подписи. Первый шаг - получить сертификат. В .NET для этого вы можете использовать класс
SslStream
.static class CertificateDownloaderИспользование:
{
//Этот метод обратного вызова нужен для проверки безопасности сертификата
//Но здесь нам не нужна проверка, мы только получаем данные сертификата
private static readonly
RemoteCertificateValidationCallback
сallback = (_, _, _, _) => true;
public static async Task<X509Certificate2?>
GetCertificateAsync(string domain, int port = 443)
{
using var client = new TcpClient(domain, port);
using var sslStream =
new SslStream(client.GetStream(), true, сallback);
// Инициируем соединение, чтобы скачать сертификат
await sslStream
.AuthenticateAsClientAsync(domain)
.ConfigureAwait(false);
var cert = sslStream.RemoteCertificate;
if (cert != null)
return new X509Certificate2(cert);
return null;
}
}
static async Task Main()Далее можно настроить повторяющуюся задачу в Azure Pipelines или GitHub Actions для ежедневной проверки сертификатов, но это выходит за рамки этого поста.
{
var cert = await
CertificateDownloader.GetCertificateAsync("www.google.com");
Console.WriteLine($"Subject: {cert?.Subject}");
Console.WriteLine($"Issuer: {cert?.Issuer}");
Console.WriteLine($"NotBefore: {cert?.NotBefore}");
Console.WriteLine($"NotAfter: {cert?.NotAfter}");
Console.WriteLine($"Algorithm: {cert?.SignatureAlgorithm.FriendlyName}");
}
Источник: https://www.meziantou.net/downloading-a-ssl-certificate-in-dotnet.htm
День девятьсот девяносто восьмой. #ЗаметкиНаПолях #AsyncTips
Обработка задач при завершении
Задача
Имеется коллекция задач, которые будут использоваться с await; требуется организовать обработку каждой задачи после ее завершения. При этом обработка каждой задачи должна происходить сразу же после завершения, без ожидания других задач.
Допустим, у нас есть некоторый набор однотипных задач. Для простоты возьмём задачу, которая просто ожидает заданное количество секунд:
Решение заключается в добавлении оборачивающего асинхронного метода, который обеспечивает ожидание задачи и обработку ее результата. Этот метод можно записать как асинхронную лямбду:
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
Обработка задач при завершении
Задача
Имеется коллекция задач, которые будут использоваться с await; требуется организовать обработку каждой задачи после ее завершения. При этом обработка каждой задачи должна происходить сразу же после завершения, без ожидания других задач.
Допустим, у нас есть некоторый набор однотипных задач. Для простоты возьмём задачу, которая просто ожидает заданное количество секунд:
async Task<int> DelayAndReturnAsync(int value)Создадим коллекцию этих задач:
{
await Task.Delay(TimeSpan.FromSeconds(value));
return value;
}
Task<int> taskA = DelayAndReturnAsync(2);Можно ожидать каждую задачу в цикле, но тогда они будут обрабатываться последовательно в порядке их нахождения в коллекции:
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
Task<int>[] tasks = new[] { taskA, taskB, taskC };
// Выводит "2", "3" и "1" вместо "1", "2" и "3".Решение
async Task ProcessTasksAsync(Task<int>[] tasks)
{
foreach (Task<int> task in tasks)
{
var result = await task;
Trace.WriteLine(result);
}
}
Решение заключается в добавлении оборачивающего асинхронного метода, который обеспечивает ожидание задачи и обработку ее результата. Этот метод можно записать как асинхронную лямбду:
async Task ProcessTasksAsync(Task<int>[] tasks)В этом решении обработка задач выполняется конкурентно, тогда как в исходном коде задачи обрабатывались по очереди. Обычно это не создаёт затруднений, но, если такой способ обработки в вашей ситуации недопустим, рассмотрите возможность использования блокировок.
{
Task[] processing = tasks.Select(async t =>
{
var result = await t;
Trace.WriteLine(result);
}).ToArray();
// Ожидаем завершения всей обработки
await Task.WhenAll(processing);
}
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
День девятьсот девяносто девятый. #ЧтоНовенького
Hot Reload Убрали Отовсюду, Кроме Visual Studio 2022… Но Потом Вернули
Суть
В прошлую среду Microsoft объявили о последних подготовках к выходу .NET 6 (кстати, презентация пройдёт на .NET Conf 2021, начало 9 ноября в 19:00 по Москве). Отдельным большим постом было объявление о релизе Hot Reload, из которого стало известно, что широко разрекламированную утилиту внезапно решили выпилить отовсюду, кроме Visual Studio 2022. То есть, она не работала бы ни в VS Code, ни в Rider, ни в консольных утилитах вроде
Развитие драмы
Решение было быстро замечено и раскритиковано чуть менее, чем всем .NET сообществом. Вот например тред от Microsoft MVP Шона Киллина. Основной посыл критиков в том, что такое решение подрывает доверие ко всей экосистеме. Более того, этот поступок был практически бессмысленным, т.к. весь код утилиты был открыт, и ничто не мешало энтузиастам взять его и запилить собственные версии. А JetBrains уже в пятницу объявили об обновлении Rider, поддерживающем Hot Reload.
В результате в субботу в Microsoft, что называется, «дали заднюю». Директор по управлению программами .NET Скотт Хантер принёс извинения сообществу и сообщил, что пулл реквест по возвращению Hot Reload в .NET SDK будет удовлетворён.
Что это было?
Да чёрт его знает. Мотивы поступка менеджеров Microsoft до сих пор не ясны. Стимулировать использование новой VS 2022? По слухам Microsoft особой прибыли от IDE не получают. Основной поток идёт от Azure. Да и вряд ли это стало бы решающим фактором при выборе рабочего процесса.
В общем очередная победа гражданского общества над корпорацией зла… Хотя, может на это и был изначальный расчёт – нагнать хайпа перед релизом .NET 6?
Источник: https://devblogs.microsoft.com/dotnet/net-hot-reload-support-via-cli/
Hot Reload Убрали Отовсюду, Кроме Visual Studio 2022… Но Потом Вернули
It’s been 0 days since .NET had drama.Такое сообщение выдаёт недавно созданная шуточная утилита dotnet-drama, которую успели написать за выходные.
Суть
В прошлую среду Microsoft объявили о последних подготовках к выходу .NET 6 (кстати, презентация пройдёт на .NET Conf 2021, начало 9 ноября в 19:00 по Москве). Отдельным большим постом было объявление о релизе Hot Reload, из которого стало известно, что широко разрекламированную утилиту внезапно решили выпилить отовсюду, кроме Visual Studio 2022. То есть, она не работала бы ни в VS Code, ни в Rider, ни в консольных утилитах вроде
dotnet watch
. Причём, поначалу было не ясно, почему было принято такое решение. Но, судя по объяснениям от Microsoft (а точнее по их отсутствию), скоро стало понятно, что решение было не техническим, а чисто менеджерским. То есть, готовую рабочую утилиту, которой многие уже начали пользоваться даже в режиме превью, решили убрать… «просто потому что».Развитие драмы
Решение было быстро замечено и раскритиковано чуть менее, чем всем .NET сообществом. Вот например тред от Microsoft MVP Шона Киллина. Основной посыл критиков в том, что такое решение подрывает доверие ко всей экосистеме. Более того, этот поступок был практически бессмысленным, т.к. весь код утилиты был открыт, и ничто не мешало энтузиастам взять его и запилить собственные версии. А JetBrains уже в пятницу объявили об обновлении Rider, поддерживающем Hot Reload.
В результате в субботу в Microsoft, что называется, «дали заднюю». Директор по управлению программами .NET Скотт Хантер принёс извинения сообществу и сообщил, что пулл реквест по возвращению Hot Reload в .NET SDK будет удовлетворён.
Что это было?
Да чёрт его знает. Мотивы поступка менеджеров Microsoft до сих пор не ясны. Стимулировать использование новой VS 2022? По слухам Microsoft особой прибыли от IDE не получают. Основной поток идёт от Azure. Да и вряд ли это стало бы решающим фактором при выборе рабочего процесса.
В общем очередная победа гражданского общества над корпорацией зла… Хотя, может на это и был изначальный расчёт – нагнать хайпа перед релизом .NET 6?
Источник: https://devblogs.microsoft.com/dotnet/net-hot-reload-support-via-cli/
День 1000.
И вот так незаметно прошла 1000 дней. Кстати, мне порядком надоело писать числа прописью, тем более что теперь это будет совсем длинно. Поэтому дальше будут цифры.
К юбилею вот подогнали книжку, над переводом которой работал, поэтому вот хвастаюсь. Купить можно тут.
Спасибо, что читаете. Отдельное спасибо за комментарии и обсуждения в чате.
Может, пора уже какой-нибудь мерч выпустить, что скажете? Давайте для пробы начнём с кружек. Напишите в комментариях, кто хочет кружку. Среди желающих выберу случайным образом несколько человек и отправлю им сувениры. Только сразу оговорюсь, будьте готовы сообщить мне реальные ФИО и адреса, куда прислать.
И вот так незаметно прошла 1000 дней. Кстати, мне порядком надоело писать числа прописью, тем более что теперь это будет совсем длинно. Поэтому дальше будут цифры.
К юбилею вот подогнали книжку, над переводом которой работал, поэтому вот хвастаюсь. Купить можно тут.
Спасибо, что читаете. Отдельное спасибо за комментарии и обсуждения в чате.
Может, пора уже какой-нибудь мерч выпустить, что скажете? Давайте для пробы начнём с кружек. Напишите в комментариях, кто хочет кружку. Среди желающих выберу случайным образом несколько человек и отправлю им сувениры. Только сразу оговорюсь, будьте готовы сообщить мне реальные ФИО и адреса, куда прислать.