.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День восемьсот восемнадцатый. #ЧтоНовенького
Эпик Фейл при Демонстрации Горячей Перезагрузки в Blazor
Во время прямой трансляции мероприятия ASP.NET Community Standup на тему «Обновления ASP.NET Core в .NET 6 Preview 3» Дэниел Рот, главный программный менеджер ASP.NET столкнулся с неожиданной проблемой при демонстрации одного из самых ожидаемых нововведений - Blazor Hot Reload.

Около четырех минут на глазах тысяч зрителей Рот пытался победить подготовленный пример. Рот и ведущий Джон Гэллоуэй изучали новые функции ASP.NET Core, обещанные в .NET 6, выпуск которого запланирован на ноябрь.

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

Однако внезапно возникла странная ошибка невозможности записи файла, которую Рот и Гэллоуэй какое-то время пытались победить при помощи комментариев от публики. Рот даже зашёл в диспетчер задач и убил один из процессов .NET, пообещав, что «нам не придётся этого делать».

Всё веселье начинается на 43й минуте ролика (ссылка выше).

Помимо горячей перезагрузки, которую обещают распространить на все виды приложений .NET, Джон и Дэниэл рассматривают и другие нововведения в .NET 6:
- MAUI
- улучшения в производительности .NET
- нативную облачную разработку
- изменения в MVC (теперь рабочий сайт можно создать буквально в 3 строчки кода)
- одностраничные приложения
- улучшения в производительности Blazor Webassembly
и многое другое.

Источник: https://visualstudiomagazine.com/articles/2021/04/23/epic-fail.aspx?m=1
День восемьсот девятнадцатый. #TipsAndTricks
Когда-то давно я писал про горячие клавиши в Visual Studio. Кроме того по тегу #TipsAndTricks можете поискать другие полезные советы. А сегодня расскажу вам о некоторых фишках Visual Studio Code. В конце концов, это самый популярный редактор кода.

Горячие клавиши
- для Mac
- для Windows
- для Linux

Если вы фанат лигатур посмотрите на бесплатные Fira Code и Cascadia Code.

Полезные расширения
1. Emmet
Это плагин для многих популярных текстовых редакторов, позволяющий генерировать стандартные блоки HTML и CSS.

2. Quokka.js
Предпросмотр значений переменных и функций JS и TypeScript во время набора кода.

3. Bracket Pair Colorizer
Подсветка соответствующих скобок разными цветами.

4. Live Server
Локальный сервер для предпросмотра создаваемых веб-страниц.

5. Settings Sync
Позволяет синхронизировать настройки, темы, иконки, горячие клавиши и т.п. на разных машинах с помощью GitHub Gist.

6. Prettier Extension
Тонкая настройка форматирования кода на разных языках.

7. Polacode
Позволяет быстро делать скриншоты кода и обрабатывать их.

8. Better Comments
Выделяет разные типы комментариев: TODO, важные замечания, документацию и т.п.

9. Markdown Lint
Проверка оформления и стилей документов MarkDown.

10. VS Code Icons
Содержит несколько коллекций иконок.

Красивые темы
1. Cobalt 2
2. Night Owl
3. One Dark Pro
4. Dracula Official
5. Winter is Coming
6. Shades of Purple
7. Material Theme
8. Rainglow
День восемьсот двадцатый. #ЗаметкиНаПолях #GC
Топ Вопросов о Памяти в .NET. Продолжение 5-8
Начало 1-4

5. Какие ограничения на размер занимаемой памяти у приложений .NET?
Ограничения размера приложения .NET такие же, как у любого процесса, ограниченного виртуальным адресным пространством - объемом памяти, который может быть выделен, исходя из разрядности приложения. И хотя в настоящее время подавляющее большинство операционных систем являются 64-битными, мы всё ещё можем компилировать/запускать наши программы и как 32-, и как 64-битные. То же самое относится к среде выполнения .NET, выполняющей наше приложение .NET. См. также про 64-битную VS 2022.

32-битному приложению может быть выделено до 4 ГБ памяти, но по умолчанию половина его предназначена для операционной системы Windows, а половина - для самого приложения. Таким образом, ограничение по умолчанию составляет 2 ГБ. Однако в случае 64-битной Windows мы можем запускать 32-битные приложения в так называемом режиме обработки больших адресов (Large Address Aware), который позволяет выделять больше памяти - около 3 ГБ. Этот флаг, например, используется при размещении 32-разрядных приложений ASP.NET Framework внутри IIS. В Linux действуют аналогичные ограничения. Таким образом, около 2 или 3 ГБ - это предел для 32-разрядного приложения .NET.

64-битному приложению теоретически можно выделить до 16 ЭБ (эксабайт!) памяти. В настоящее время большая часть оборудования использует только 48 бит для выделения верхних и нижних 128 ТБ всего адресного пространства (для операционной системы и приложения). Это предел для приложений .NET.

6. Что такое LOH (Large Object Heap)?
Куча больших объектов - это специальная часть управляемой кучи. С самого начала .NET порог по умолчанию для обработки объекта как «большого» составляет 85000 байт. Недавно добавилась возможность увеличить этот лимит. Каждый большой объект выделяется в LOH и остаётся там до тех пор, пока не будет собран мусор.

LOH отделён от SOH (Small Objects Heap – кучи малых объектов), потому что «большие» объекты имеют другие характеристики: их создание и перемещение (сжатие) памяти может повлечь за собой большие накладные расходы. Поскольку ожидается, что таких объектов будет немного, затраты на их размещение выше (включая очистку памяти и некоторые накладные расходы на многопоточную синхронизацию). Т.к. нет другого способа очистки LOH, кроме полной сборки мусора, если ваше приложение часто размещает большие объекты и приводит к нехватке памяти, оно может приводить к дорогостоящим полным сборкам мусора.

7. Для чего нужна утилита dotnet trace?
dotnet trace - это один из инструментов командной строки (CLI) для сбора различных «диагностических трассировок» процесса .NET. Вы можете использовать три предопределенных профиля:
- cpu-sampling - выборка профилировщика CPU для наблюдения за использованием процессора,
- gc-collect - отслеживание высокоуровневых данных сборщика мусора с очень низкими накладными расходами,
- gc-verbose – то же, что выше, плюс грубая выборка распределения объектов. Созданная трассировка может быть затем открыта в Visual Studio или в инструменте PerfView.

8. Для чего нужна утилита dotnet gcdump?
dotnet gcdump - еще один инструмент диагностики, который может запускать сборщик мусора и записывать специальные диагностические данные, выдаваемые во время него. Это позволяет создавать «gcdump» - не обычный дамп памяти, содержащий всю или часть памяти процесса, а «диагностический снимок» состояния управляемой памяти. Он включает информацию о том, какие управляемые объекты были обнаружены и собраны во время сборки мусора (без содержания этих объектов). Поэтому «gcdump» намного меньше, чем снимок всей памяти, потребляемой процессом. Его можно проанализировать в Visual Studio или PerfView (включая информацию об отношениях между объектами).

Продолжение следует…

Источник:
https://dotnetmemoryexpert.com
День восемьсот двадцать первый. #ВопросыНаСобеседовании
4 Частых Ошибки на Собеседовании
Всех с предпраздничной пятницей, дорогие подписчики. На сегодня приготовил вам тему для обсуждения. Основан пост на недавно вышедшем видео Клемента Михайлеску, который какое-то время проработал интервьюером в Google. Его список ошибок основан на личном опыте. Понятно, что наш опыт может отличаться от тамошнего, поэтому добро пожаловать в комментарии. Продолжайте список того, чего не стоит делать на собеседовании.

Приведённые ниже ошибки относятся к решению задач на собеседовании. Они общие для всех интервью и не затрагивают каких-то узких технических моментов.

1. Излишняя болтливость
Коммуникабельность очень важна для программиста. Важно умение выражать и доносить свои мысли. Но всё хорошо в меру. Не стоит озвучивать все свои мысли. Беспорядочный поток сознания собьёт интервьюера. Сообщать нужно уже готовые, «продуманные» мысли. Даже если вас просят озвучивать ваши размышления, лучше попросите минуту-другую и отсейте хотя бы самые бредовые идеи, озвучив лучшее, что сможете придумать.

2. Угадывание
Разновидность первой ошибки: если застрял - вываливать всё, что знаешь. То есть вместо объяснения, что конкретно вызывает затруднение, предлагать беспорядочный набор известных вам структур данных, алгоритмов, библиотек и т.п., которые могу иметь, но чаще всего не имеют никакого отношения к решению задачи.
Интервьюеру важнее понять, насколько вы можете мыслить логически. Его вряд ли впечатлит простой перебор всего, что вы знаете. Это скорее наведёт его на мысль, что вы вообще не понимаете, о чём говорите. Лучше выбрать хоть сколько-нибудь подходящий вариант решения и объяснить, почему вы его выбрали, даже если он в итоге окажется неверным.

3. Спешка в написании кода
Это также часто случается и в работе. Не стоит, решая задачу, бросаться писать какой-нибудь код. Чаще всего код при этом получается плохо структурированный, непонятный и с кучей ошибок. В результате рано или поздно запутаетесь и вы, и интервьюер. Опять же, попросите минуту-другую времени, продумайте структуру программы. Здесь может очень помочь Процесс Программирования с Псевдокодом.

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

Источник: https://youtu.be/nf6vrGcGDSI
День восемьсот двадцать второй. #Юмор

Помните эту шутку на первое апреля? Так вот, в Stack Overflow заморочились и решили реально посчитать, как часто люди копипастят код с их сайта. Получилось, что каждый четвертый посетитель сайта, копирует что-то в течение пяти минут после перехода на страницу.

Так что, перестаньте корить себя за копирование кода! Зачем изобретать велосипед, если всю тяжелую работу уже проделал кто-то другой? Этот сайт основан на повторном использовании знаний.

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

Источник: https://stackoverflow.blog/2021/04/19/how-often-do-people-actually-copy-and-paste-from-stack-overflow-now-we-know
День восемьсот двадцать третий.
Snoop

Сегодня предлагаю вам посмотреть третье видео из серии Open-Source Power-Ups про популярные проекты с открытым исходным кодом в сообществе .NET.

Запись вебинара «OSS Power-Ups: Snoop»

Snoop - это швейцарский армейский нож, когда дело касается анализа пользовательского интерфейса в WPF. В отличие от диагностики XAML внутри Visual Studio, его можно легко установить и использовать на любом компьютере - даже на сайте заказчика - без сложной установки. Бастиан Шмидт, нынешний сопровождающий проекта, даст нам хорошее представление о нём. В видео показано, как просматривать и изменять значения свойств, проверять триггеры из стилей и шаблонов, диагностировать ошибки привязки представлений и другие распространенные ошибки в WPF, устранять неполадки в событиях и выяснять, где они обрабатываются, предварительно просматривать и масштабировать части пользовательского интерфейса (даже в 3D) и многое другое.
День восемьсот двадцать четвёртый. #ЗаметкиНаПолях
Как Обратиться к HttpContext в
ASP.NET Core
Как правило, переход от веб-форм или из MVC5 в ASP.NET Core требует значительного рефакторинга. Например, HttpContext.Current был удалён в ASP.NET Core. Доступ к текущему контексту HTTP из отдельной библиотеки классов – это один из примеров беспорядочной архитектуры, которых ASP.NET Core пытается избежать. Есть несколько способов получить текущий контекст запроса в ASP.NET Core.

Что такое HttpContext?
Это объект, содержащий информацию о текущем Http-запросе: авторизация, аутентификация, объекты запроса и ответа, сессию, текущего пользователя, отправленные данные формы и т.п. Каждый Http-запрос создаёт новый объект HttpContext с актуальной информацией.

1. Доступ к HttpContext из контроллера?
Контроллеры предоставляют свойство ControllerBase.HttpContext, чтобы мы могли напрямую обращаться к контексту текущего Http-запроса:
[HttpGet("/getDetails")]
public string GetDetails() {
var result =
"Method: " + HttpContext.Request.iss.onethod +
" Path: " + HttpContext.Request.Path;
return result;
}
Вывод:
Method: GET Path: /getdetails

2. Доступ к HttpContext из промежуточного ПО
Если вы пишете собственный вариант промежуточного ПО, то контекст текущего запроса передаётся в метод Invoke/InvokeAsync:
public async Task InvokeAsync (HttpContext context) {

}

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

Шаг 1. Зарегистрируйте зависимость и сервис:
public void ConfigureServices
(IServiceCollection services) {

services.AddHttpContextAccessor();
// до .NET Core 2.2 используйте
//services
// .TryAddSingleton<IHttpContextAccessor,
// HttpContextAccessor>();

services.AddTransient<IUserService, UserService>();

}

Шаг 2: внедрите IHttpContextAccessor в конструктор сервиса:
public class UserService : IUserService {
private IHttpContextAccessor
_httpContextAccessor;

public UserService(IHttpContextAccessor hca)
{
_httpContextAccessor = hca;
}

public string GetLoginUserName() {
return _httpContextAccessor
.HttpContext.User.Identity.Name;
}
}

Источники:
-
https://www.telerik.com/blogs/how-to-get-httpcontext-asp-net-core
-
https://stackoverflow.com/questions/38571032/how-to-get-httpcontext-current-in-asp-net-core
👍1
День восемьсот двадцать пятый. #DeveloperPath
Новости проекта «Путь Разработчика»
О проекте

Очередная серия новостей проекта.
1. Релиз наладили, даже кое-какое CI/CD прикрутили.
2. Проект выложен, и даже получил домен https://www.developer-path.com/
Пока там ничего особо нет, только краткое описание и форма входа. Постепенно будем пилить UI. Так что, если есть веб-дизайнеры, желающие присоединиться, нарисовать лого, айдентику, интерфейс, вот это всё, будем очень признательны.
3. API тоже доступно публично, пока тут https://developerpathapi.azurewebsites.net/index.html
Пожалуйста, не ломайте ничего :)

Как уже писал раньше, задачи найдутся для самых разных специалистов. Если кто-то хочет поковыряться в .NET 5, C#9, Blazor, паттерне CQRS или веб-дизайне, милости просим.

Проект всё также на Azure DevOps: https://dev.azure.com/sbenzenko/DeveloperPath/
Он открытый, поэтому посмотреть, почитать можно и без регистрации. Авторизованные через аккаунт Microsoft или GitHub могут оставлять комментарии или добавиться в команду. Если вы ещё не в команде, пишите в личку или в комментарии свой e-mail, я вас приглашу.

Задачи можно посмотреть в Azure DevOps.

Помимо этого, вы, конечно, можете просто взять код проекта на GitHub и предложить что-то своё.

По всем вопросам добро пожаловать в комментарии сюда, либо в проект на Azure DevOps.
День восемьсот двадцать шестой. #ЗаметкиНаПолях #GC
Топ Вопросов о Памяти в .NET. Продолжение 9-12
Начало 1-4
Продолжение 5-8

9. Что обязательно будет содержать дамп памяти любого приложения .NET?
Забавный факт, который иногда может вводить в заблуждение при анализе дампов памяти: каждый процесс .NET заведомо выделяет экземпляры OutOfMemoryException и StackOverflowException. Просто на случай, если они понадобятся, что будет означать действительную нехватку памяти и отсутствие возможности их создать!
Кроме того, некоторые массивы object[] предварительно выделяются в LOH для ссылок на объекты, используемые «дескрипторами». Это важный механизм, используемый интернированием строк, статикой и локальной статикой потока. Так, например, даже если вы вообще не используете LOH, вы увидите там несколько массивов, ссылающихся на ваши статические объекты.

10. Что означает выражение "stop the world" в контексте сборки мусора?
Каждый запуск сборки мусора в .NET имеет несколько коротких и длинных фаз, когда ожидается, что среда выполнения «приостановит» приложение. Эти паузы также известны как фазы «остановки мира» ("stop the world"). И в целом они явно нежелательны, так как влияют на скорость отклика и производительность приложения. Многие улучшения .NET GC делают эти паузы как можно короче.
Итак, «остановка мира» - это пауза на время выполнения сборки мусора (или её части), когда все управляемые потоки «приостановлены». Например, это позволяет получить единообразное представление о том, какие управляемые объекты используются. Неуправляемые потоки не приостанавливаются, поскольку в этом нет необходимости - по умолчанию они не обращаются к управляемым объектам, и даже если это так, они обращаются к «закреплённым» (pinned) объектам, которые обрабатываются особым образом.

11. Как работает параллельная сборка мусора в .NET 5?
Параллельная (фоновая) сборка мусора большую часть времени работает одновременно с управляемыми потоками, не создавая длительных пауз для «остановки мира». Это не означает, что пауз вообще не бывает. Требуется как минимум две паузы: в начале и где-то посередине, чтобы получить некоторое согласованное представление о состоянии памяти.
Более того, текущая реализация .NET GC не может сжимать память во время работы приложения. Таким образом, как полная сборка мусора, так и сборка в поколениях 0 и 1, должны работать в режиме «остановки мира», если требуется сжать память.
Работа над внедрением параллельного сжатия памяти определённо продолжается, и рано или поздно мы можем ожидать его добавления в экосистему .NET. Такие реализации успешно используются, например, в экосистеме JVM. Тем не менее, у них есть свои недостатки и компромиссы, поскольку ничего не даётся бесплатно.

12. Как параметры нужно учитывать, чтобы оценить потребление памяти вашей программой?
Чтобы иметь хорошее общее представление о потреблении памяти вашей .NET-программой, лучше всего знать как минимум три величины.
1. Сколько памяти выделено вашей программе в целом (частные байты - private bytes).
2. Сколько её физически потребляется в RAM («рабочий набор» - working set) - это позволит заметить случаи, когда страницы памяти выгружаются на диск.
3. В контексте .NET полезно знать, сколько памяти потребляется в управляемой куче (Managed Heap).

Что же касается виртуального адресного пространства (virtual address space), оно охватывает всё адресное пространство, которое может использоваться нашим приложением, и не связано напрямую с поведением приложения.

Продолжение следует…

Источник:
https://dotnetmemoryexpert.com
День восемьсот двадцать седьмой. #ЧтоНовенького
.NET 6: Улучшения в LINQ. Начало 1/2
Продолжаем изучать, что нас ждёт в релизе .NET 6. На этот раз про LINQ.

Индексные операции в IEnumerable<T>
Первоначально IList<T> отличался от IEnumerable<T> наличием обращения по индексу. Идея заключалась в том, что только коллекции, поддерживающие быстрые операции с индексами ~O(1), могут реализовывать IList<T>. Теоретически индексные операции над IEnumerable<T> не поощрялись, поскольку считались медленными.

С появлением LINQ многие из этих предположений позабылись. Методы расширения, такие как Enumerable.Count() и Enumerable.ElementAt() доступны на любой коллекций, даже если в реальности они выполняются за O(N) время. Новые методы расширения для IEnumerable (ElementAt, ElementAtOrDefault и Take) продолжают этот тренд, позволяя использовать индексы и диапазоны:
var elements = source.Take(range: 10..^10);

Операции подсчёта в IEnumerable<T>
Когда на IEnumerable<T> вызывается Count(), происходят две вещи. Сначала библиотека LINQ пытается привести коллекцию к интерфейсу, который предоставляет свойство Count. Если это невозможно, метод выполняет итерацию по всей коллекции, считая элементы.

Для больших коллекций такой подсчет может быть очень дорогим, особенно для IQueryable при запросе к базе данных. Поэтому разработчики попросили «безопасную» функцию подсчета. Эта функция проверяет наличие быстрого свойства Count и, если не может его найти, ничего не возвращает:
public static bool TryGetNonEnumeratedCount(this IEnumerable<T> source, out int count);
Длинное имя TryGetNonEnumeratedCount как бы намекает, что вы делаете что-то не то. В идеале любой API, возвращающий список, должен возвращать либо коллекцию со строгим именем, либо интерфейс более высокого уровня, такой как IList<T> или IReadOnlyList<T>.

Трёхсторонний Zip
Метод расширения Zip объединяет две коллекции, перечисляя их одновременно. Например, если у вас есть список 1, 2, 3 и список A, B, C, то результирующая коллекция будет состоять из кортежей (1, A), (2, B), (3, C). Теперь предложено расширить метод для объединения трёх коллекций за раз.

Разбиение на партии
Часто требуется разбить коллекцию на несколько партий. Например, может оказаться, что вставка 100 строк в базу за раз быстрее, чем вставка по одной или всех сразу. Хотя этот код нетрудно написать самому, он, как правило, подвержен ошибкам. Легко ошибиться в последней партии, если количество строк не делится равномерно на размер партии. Для этого добавлен метод расширения Chunk для IEnumerable и IQueryable:
public static IEnumerable<T[]> Chunk(this IEnumerable<T> source, int size);
public static IQueryable<T[]> Chunk(this IQueryable<T> source, int size);

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

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

Предложение правильного использования AsParallel() относится к случаям, когда AsParallel() следует за операцией перечисления коллекции. Хотя это неочевидно, AsParallel() должен использоваться перед любыми операциями, которые можно распараллелить, такими как отображение и фильтрация. В этом случае также будет выдано предупреждение компилятора.

Окончание следует…

Источник:
https://www.infoq.com/news/2021/04/Net6-Linq/
День восемьсот двадцать восьмой. #ЧтоНовенького
.NET 6: Улучшения в LINQ. Окончание 2/2
Начало

Операторы *By
Это методы DistinctBy, ExceptBy, IntersectBy, UnionBy¸ MinBy и MaxBy. Первые 4 позволяют работать с коллекциями как со множествами. Для них предусмотрен делегат keySelector, возвращающий ключ для сравнения. Методы позволят выполнять операцию сравнения на подмножестве данных, а не на всей коллекции. Их можно использовать для повышения производительности или для обеспечения настраиваемого поведения без потери исходных данных. Например, имея коллекции всех заказов и завершённых заказов:
IEnumerable<Order> all = GetAll();
IEnumerable<Order> completed = GetCompleted();
Получить незавершённые заказы можно с помощью ExceptBy, сравнивая элементы по свойству ID:
IEnumerable<Order> remaining =
all.ExceptBy(completed, o => o.ID);
В отличие от варианта с использованием Where, который менее интуитивно понятен и выполняется за квадратичное время (O(M*N)).

В MinBy и MaxBy вместо селектора ключей предоставляется селектор элементов. Они вернут элемент коллекции с наибольшим/наименьшим значением соответственно, в отличие от существующих Min и Max, которые возвращают само значение. Кроме того, в каждый из методов (в том числе и Min и Max) можно будет передать необязательный объект-компаратор.
Сигнатуры всех методов похожи, приведу для примера DistinctBy и MinBy с компаратором:
public static IEnumerable<TSource> 
DistinctBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector);

public static TSource
MinBy<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector,
IComparer<TResult>? comparer);

Улучшение в *OrDefault
Вариант оператора *OrDefault используется для предоставления значения по умолчанию, когда Single, First или Last передаётся пустая коллекция. В этой функции теперь можно будет переопределить возвращаемое значение по умолчанию:
public static TSource 
SingleOrDefault<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate,
TSource defaultValue);

Примечание: обратите внимание, что во всех описанных выше методах расширения есть как версия IEnumerable, так и версия IQueryable. Все новые API, возвращающие IEnumerable, должны включать соответствующую версию IQueryable. Это не позволит пользователю API преобразовать запрос в обычное перечисление, не осознавая этого.

Источник:
https://www.infoq.com/news/2021/04/Net6-Linq/
День восемьсот двадцать девятый. #Testing
Несколько хороши ваши тесты? Протестируйте их с помощью мутатора Stryker
Вы взялись за новый проект и решили всё сделать правильно. Возможно даже разрабатывать через TDD. И вот, всё готово, все тесты зелёные, покрытие тестами полное, вы гордитесь своей работой. Затем от клиента приходит запрос на новый функционал. Вы что-то добавляете, также решаете что-то отрефакторить, т.к. лучше поняли домен и требования клиента, добавляете новые тесты, все тесты (и старые, и новые) проходят, всё прекрасно.

И тут приходит сообщение от клиента… В смысле «не работает»???

Внезапно вы находите ошибку, и не в новом функционале, а в исходной версии кода. Но как же так? Все тесты проходили. Оказывается, вы не охватили тестами все случаи, несмотря на 100% покрытие. Затем вы изменили кое-что здесь и там, и в результате функциональность уже не работает должным образом. Как снизить вероятность повторения такой ситуации?

Что такое мутационное тестирование?
Мутационное тестирование - это введение ошибок (мутантов) в ваш производственный код. Например, равенство заменяется неравенством, AND на OR и т.п. Затем выполняются тесты для каждого мутанта, и тесты должны провалиться! Неудача означает, что мутант убит. Если тесты проходят, мутант выжил, а значит, есть ложноположительные результаты.

Stryker делает всё это автоматически. Он поддерживает различные мутаторы, например арифметические операторы, равенство, логические операторы, мутации строк и даже LINQ. Вы можете посмотреть полный список доступных мутаторов в документации.

Stryker устанавливается как dotnet tool глобально или для каждого проекта в отдельности.
dotnet tool install dotnet-stryker
Затем он запускается из папки юнит-тестов
dotnet stryker
и создаёт HTML отчёт с результатами мутационного тестирования (см. в верхней части рисунка ниже). Как видите, только 3 «мутанта» были убиты, а 23 выжили, и мы получаем результат мутационных тестов всего в 11,54%. Это значит, что нам нужно добавить юнит-тестов в систему и покрыть тестами случаи, когда мутанты выживают. Можно «провалиться» в каждую папку и каждый отдельный файл из отчёта, и посмотреть, какие мутационные тесты были выполнены, и их результат. Например, в нижней части рисунка мы видим «выжившего мутанта». То есть, у нас нет теста, проверяющего случай, когда _orderStatusId не равен OrderStatus.Submitted.Id, и нам нужно его добавить.

Отдельный интересный пример – замена LINQ методов, например, First/Single на FirstOrDefault/SingleOrDefault и наоборот. Здесь проверяется случайное неверное использование. В случае пустой коллекции First/Single должны выбрасывать исключение, тогда как *OrDefault версии нет.

Итого
Вы можете спросить: когда следует запускать мутационные тесты? В CI/CD, локально или время от времени?

Пока не понятно. Я всё ещё новичок в мутационном тестировании. На данный момент думаю, имеет смысл добавить его как часть разработки. То есть, начиная работу с некоторым кодом, запустить Stryker, чтобы посмотреть хорошо ли код покрыт тестами. Если да, можно уверенно работать с этим кодом. Если нет - то сначала сосредоточьтесь на увеличении охвата, а затем на развитии кода. Сделать Stryker частью вашего CI/CD также возможно.

Stryker и мутационное тестирование в целом - не серебряная пуля. Я бы скорее назвал его страховкой. Закончили кодирование функции или исправление ошибки? Сделайте одолжение себе и своей команде и проверьте, охватывают ли ваши тесты все возможные случаи. Да? Отлично. Нет? Просто исправьте это.

Источник: https://lukaszcoding.com/how-good-are-your-net-tests-test-your-tests-with-stryker-mutator/
👍2
День восемьсот тридцатый. #ЗаметкиНаПолях #GC
Топ Вопросов о Памяти в .NET. Продолжение 13-16
Начало 1-4
Продолжение 5-8
Продолжение 9-12

13. Что такое POH?
Куча закреплённых объектов (Pinned Object Heap) - это новый тип раздела управляемой кучи (помимо SOH и LOH), добавленный в .NET 5 и предназначенный для объектов, которые не будут перемещаться сборщиком мусора. Таким образом, POH никогда не уплотняется, что позволяет рассматривать все объекты в POH как «закреплённые» по умолчанию. Это полезно, потому что закрепление в SOH/LOH вводит некоторые накладные расходы и снижает гибкость сжатия памяти (что может привести к фрагментации). Наличие специального места для «закреплённых» объектов устраняет эту проблему.
Однако для аллокаций памяти был добавлен новый API, потому что теперь нам нужно указать, что мы выделяем объект, который необходимо немедленно закрепить. В настоящее время это можно сделать с помощью GC.AllocateUninitializedArray. То есть мы можем выделить там только массив типов, которые не являются ссылочными и не содержат ссылок (например, массив байтов). Хотя собственно POH не имеет таких ограничений.

14. Какую сборку мусора мы можем вызвать через API GC?
В .NET доступны API, позволяющие контролировать поведение сборщика мусора. Например, GC.Collect и его перегрузки позволяют нам запускать сборку мусора в поколениях 0, 1 или полную (с указанием, хотите ли вы, чтобы она была блокирующей и/или с уплотнением памяти). Однако из-за того, как работает сборка мусора в .NET, мы не можем запустить сборку только поколения 2 или только LOH. При сборке поколения (или LOH) всегда выполняется сборка мусора для всех младших поколений. Поэтому, когда мы запускаем сборку в поколении 2, мы запускаем полную сборку мусора. И нет API для запуска только сборки в LOH (в этом случае также будет запущена полная сборка).
С другой стороны, действительно есть возможность запустить сжатие LOH при следующей непараллельной полной сборке мусора вот таким способом:
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();

15. Используются ли память и сборщик мусора разными .NET приложениями совместно?
Когда мы запускаем приложение .NET, оно выполняется средой выполнения .NET внутри обособленного процесса. Другими словами, каждое приложение .NET выполняется своим собственным экземпляром среды выполнения, и между ними нет ничего общего. Это позволяет нам настраивать и управлять жизненным циклом каждого приложения .NET отдельно. Это касается и сборщика мусора, и управляемой кучи. Каждый процесс имеет свой собственные кучу и сборщик мусора и не знает о существовании других. Сборщик мусора, запускаемый в одном приложении .NET, не влияет (и не запускает) сборщик мусора в другом приложении .NET.
При этом процессы, запущенные на одном компьютере, совместно используют некоторые ресурсы, такие как ЦП, память и диски. Очевидно, что на этом уровне может произойти некоторое совместное использование ресурсов.

16. Каково предназначение флага gcAllowVeryLargeObjects?
Если вы когда-либо достигали предела общего размера объекта в 2Гб - возможно, массива или строки, хорошей новостью для вас является то, что вы можете преодолеть это ограничение с помощью флага gcAllowVeryLargeObjects (по умолчанию отключен). Он позволяет создавать массивы размером более 2Гб, но применим только к массивам (то есть не применим к строкам или другим типам объектов) и только к общему размеру массива. Максимальное количество элементов в массиве (UInt32.MaxValue) не меняется. И хотя это, конечно, не самое распространённое требование, например, оно может быть полезно в приложениях, обрабатывающих огромные изображения или большие данные.

Продолжение следует…

Источник: https://dotnetmemoryexpert.com
День восемьсот тридцать первый. #Юмор
9 Квинтиллионов Остановок
Пост немножко боян, но всё-таки, довольно смешно. Итак, в твиттере выложили скриншот приложения Journey Tracker, используемого в Англии, в котором показан путь поезда с 9,223,372,036,854,775,807 остановок. Как такое возможно?

Программисты, наверное, сразу попробуют сравнить это число со степенью двойки, и действительно, оно всего на 1 меньше 2^63, что очень похоже на 64-битное число со знаком. То есть имело место простое переполнение.

На самом деле на приведённой картинке был первый случай. Через пару лет случился похожий (который подробно описан в видео по ссылке ниже). Отдельно доставило, что на второй твит ответил один из разработчиков приложения: «Я думал, мы это пофиксили давным давно.»

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

Источник: https://youtu.be/48QQXpbTlVM
День восемьсот тридцать второй. #ЗаметкиНаПолях
Добавляем MVC в приложении
ASP.NET Core
Сегодня обсудим методы AddController(), AddMvc(), AddControllersWithViews() и AddRazorPages() в приложениях ASP.NET Core. Всё это методы расширения IServiceCollection. Они используются в методе ConfigureServices() класса Startup. На первый взгляд, всё вроде очевидно, но что же конкретно делает каждый из них, и когда какой использовать?

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

Все методы поддерживают:
- Контроллеры
- Связывание модели
- Авторизацию
- Валидацию

AddController() помимо общих функций поддерживает:
- API Explorer
- CORS
- Форматирование вывода

AddRazorPages() помимо общих функций поддерживает:
- Antiforgery (защита от атак CSRF)
- TempData
- Представления
- Страницы (Razor Pages)
- Тег-хелперы
- Кеш в памяти

AddControllersWithViews() поддерживает всё, что есть в AddController() и AddRazorPages(), кроме Страниц (т.е. только чистый MVC).

AddMvc() поддерживает всё, включая Страницы.

Основные моменты
- Если вам нужны только контроллеры, включая привязку модели (выбор подходящего метода действия контроллера), авторизацию и валидацию данных, вы можете использовать любой из методов.
- ApiExplorer содержит функции для обнаружения и предоставления метаданных о вашем приложении MVC. Он используется для предоставления таких сведений, как список контроллеров и действий, их URL-адреса и разрешенные методы HTTP, параметры и типы ответов. Например, его использует Swagger для генерации документации API. ApiExplorer доступен во всех методах, кроме AddRazorPages().
- CORS позволяет серверу ослабить политику одного источника (same-origin policy). Система безопасности браузера запрещает клиентскому коду страницы делать запросы к домену, отличному от того, который обслуживает веб-страницу. Это ограничение называется политика одного источника. Она предотвращает чтение вредоносным сайтом конфиденциальных данных с другого сайта. Иногда может потребоваться разрешить другим сайтам выполнять запросы между разными доменами в приложении. За это отвечает CORS, который доступен везде, кроме AddRazorPages().
- Форматирование вывода (Formatter Mapping) используется для возможности выводить ответ в различных форматах, таких как JSON, XML и т.п. Опять же, доступно везде, кроме AddRazorPages().
- Функция кэширования в памяти через IMemoryCache недоступна только в методе AddControllers(), но доступна в остальных трёх.

Какой метод использовать для вашего приложения?
Это зависит от того, какое приложение вы хотите создать.
- Если вы хотите создать приложение веб-API без представлений, используйте AddControllers().
- Если вы хотите работать с приложением на Razor Pages, используйте AddRazorPages().
- Если вы хотите использовать MVC, используйте AddControllersWithViews(). А если в этом приложении вам требуются и Razor Pages, используйте AddMVC().

Таким образом, AddMvc() включает все возможности. То есть вы можете использовать любой тип приложения (веб-API, MVC и Razor Pages). Однако, имейте в виду, что использование метода AddMvc() добавляет все функции, даже если они вам не требуются, что может повлиять на производительность приложения.

Источник: https://dotnettutorials.net/lesson/difference-between-addmvc-and-addmvccore-method/
День восемьсот тридцать третий. #ЧтоНовенького
.NET 6: Минималистические API
В Microsoft всерьёз озаботились слишком высоким порогом входа в ASP.NET, по сравнению с другими фреймворками. Проблема в том, что минимальный проект API или веб-сайта в ASP.NET содержит с десяток файлов и папок: файлы конфигурации, Startup.cs и т.п. А кто знает точно, что вообще происходит в файлах Program.cs и Startup.cs? Зачем там весь этот код?

В итоге в Microsoft пришли к следующему коду в файле Program.cs:
var app = WebApplication.Create(args);

app.Map("/", () => "Hello World");

app.MapGet("/todos", async () =>
{
using var db = new TodoDbContext();
return await db.Todos.ToListAsync();
});

app.MapPost("/todos", async (Todo todo) =>
{
using var db = new TodoDbContext();
await db.Todos.AddAsync(todo);
await db.SaveChangesAsync();
});

app.Run();

Помимо этого, конечно, нужны классы домена и контекста TodoDbContext. И понадобится ещё файл Imports.cs, где будут находиться все директивы using, используемые в приложении (их теперь, чтоб не писать в каждом файле, можно подключить глобально через global using …, по аналогии с файлом ViewImports).

И всё! Вот полноценное рабочее веб-API приложение (можно дальше добавлять MapDelete, MapPatch и т.п.). Как вам?

Никаких CreateHostBuilder, UseStartup, Configure и ConfigureServices, папки контроллеров и прочего шаблонного кода. Все настройки по умолчанию собраны в WebApplication.Create, а дальше вы просто сопоставляете путь с функцией обработчиком, и вуаля!

На мой взгляд, это великолепное нововведение! Как объясняют создатели (ссылка на полное видео внизу), это позволит новичкам не погружаться сразу в бессмысленный набор файлов, а начать с самого простого. Один файл. Задаёшь путь и нужный вывод (см. "Hello World" выше), переходишь по этому пути, и получаешь результат!

При этом самое главное, что это не обязаловка. Нынешний хорошо структурированный расклад не только продолжит существовать, но также будут добавлены все возможные вариации между ним и этим однофайловым кодом. То есть, вместо WebApplication.Create вы можете создать построитель, и внедрить нужные зависимости:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddDbContext<TodoDbContext>(o =>
o.UseSqlite("DataSource=Todos.db"));
var app = builder.Build();

Далее можно более детально настроить хост, добавить необходимые сервисы. Затем можно выделить настройку сервисов в файл Startup, а сопоставление путей с методами в отдельный файл, а дальше разделить на контроллеры. То есть обучение технологии теперь будет проходить более естественно. Не начиная с многостраничного объяснения, что, где и зачем лежит. Теперь можно начать с элементарного рабочего кода, а дальше (при необходимости) выделять обособленные части в отдельные файлы и постепенно перейти к той структуре, которая нам всем известна.

Ну и конечно, такой подход сильно поможет в создании микросервисов, когда нужен самый простой код, «без ансамбля». Помимо прочего, судя по первым тестам, он работает на 30-50% быстрее стандартного приложения из-за избавления от всего ненужного функционала.

Источник: https://youtu.be/enAskgcF0c0
День восемьсот тридцать четвёртый. #ЧтоНовенького
Обновление Ветки из Upstream в GitHub
В UI GitHub появилась кнопка, которая позволяет вам обновить вашу fork-ветку из основной upstream-ветки в два клика, вместо того, чтобы писать команды в консоли.

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

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

Источник: https://ardalis.com/github-fetch-upstream/
День восемьсот тридцать пятый. #ЗаметкиНаПолях #GC
Топ Вопросов о Памяти в .NET. Продолжение 17-20
Начало 1-4
Продолжение 5-8
Продолжение 9-12
Продолжение 13-16

17. Зачем классу финализатор?
Финализатор - это специальный метод, который вызывается средой выполнения после того, как объект будет помечен как недостижимый. Другими словами, это специальный метод, вызываемый перед сборкой мусора. Таким образом, он рассматривается как подстраховка для высвобождения некоторых неуправляемых ресурсов для объектов, содержащих их. Обычно это какой-нибудь «дескриптор» файла, сокета или что-то подобное. Предпочтительным способом является использование контракта IDisposable и освобождение этих ресурсов путем явного вызова Dispose (или с помощью директивы using). Но если программист забудет это сделать, финализатор придёт на помощь. Таким образом, классу может потребоваться финализатор, чтобы убедиться, что неуправляемый ресурс, которым он владеет, будет очищен. Финализатор, хотя обычно используется вместе с IDisposable, никак не связан с ним.
Заметьте, что
- точное время, когда именно будет вызван финализатор, не определено.
- порядок вызова финализаторов в объектах (даже связанных друг с другом) не гарантируется.

18. Можно ли узнать о сборке мусора или управлять ей?
К сожалению, в настоящее время нет доступного API, который будет информировать о происходящей в данный момент сборке мусора. Более того, нет даже надёжного API, который предупредит нас о грядущей сборке. Существует метод GC.RegisterForFullGCNotification, который (как сказано в документации) выдаёт уведомление, «когда среда выполнения чувствует, что приближается полная сборка мусора». Но это «чувство» не является точным и также зависит от магического порога, который придётся тонко настроить в соответствии с рабочей нагрузкой вашего приложения методом проб и ошибок. Этот метод не рекомендуется использовать.
С другой стороны, есть метод GC.Collect для запуска сборки мусора и менее известный метод GC.TryStartNoGCRegion, чтобы попытаться отключить сборку мусора на заданное количество аллокаций памяти.

19. Зачем используется управляемый указатель (ref)?
Управляемый указатель (также известный как ref или byref) - это особый тип указателя на разные места в памяти: данные стека, внутренние объекты или массивы или неуправляемые данные. И поскольку он может указывать на данные стека, есть серьёзные ограничения на то, где мы можем его использовать. Например, в настоящее время запрещено использовать его в качестве поля класса, потому что он может прожить дольше, чем адрес в стеке, на который он указывает. Запрещено даже использовать его в качестве поля структуры (всегда живущей в стеке), хотя это ограничение может быть ослаблено в будущем.
Управляемый указатель довольно часто используется в низкоуровневом производительном коде для непосредственного управления данными, особенно в сочетании со структурами и/или объектами Span<T> (которые, кстати, под капотом также используют управляемые указатели).

20. Чем отличается серверная сборка мусора?
Серверная сборка мусора - это режим, предназначенный для приложений с одновременной обработкой запросов, как правило, веб-приложений. Но её с успехом можно использовать и в любом другом типе приложений с аналогичными характеристиками (например, сервисом, обрабатывающим сообщения из очереди). То есть в приложениях, где нас больше всего волнует пропускная способность - обработка как можно большего количества запросов в секунду. Таким образом, в отличие от сборки мусора в режиме рабочей станции, серверная не так сильно озабочена паузами (задержкой), потому что нет прямого пользователя (например, взаимодействующего с пользовательским интерфейсом), на которого они повлияют.
И поскольку мы предполагаем, что приложение «серверное», оно также более охотно потребляет память и потоки ЦП для достижения своих целей.

Продолжение следует…

Источник:
https://dotnetmemoryexpert.com
День восемьсот тридцать шестой. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
85. Простые системы и закон Галла
Когда вам нужно будет создать новую большую систему, чтобы заменить старую, помните о законе Галла и о пользе эволюционного развития и частой обратной связи.

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

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

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

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

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

Экстремальное программирование
Одна из ключевых ценностей экстремального программирования (XP) - это простота в сочетании с практикой простого дизайна. По сути, практика XP вращается вокруг частой доставки работающего ПО и его изменения по мере необходимости для удовлетворения текущих потребностей клиентов или пользователей. Экстремальное программирование серьезно относится к закону Галла и встраивает его в процесс разработки.

Архитектура, Монолиты, Микросервисы
Мы можем попробовать применить закон Галла к архитектуре ПО, особенно для крупных и амбициозных проектов. Вместо того, чтобы пытаться написать подробную спецификацию большой системы, которую мы хотим переписать, часто лучше определить блоки существующей системы, которые хорошо работают, и переносить их в новую систему по частям, сохраняя при этом их работоспособность. Или, если это нецелесообразно, заменить существующую функциональность новой функциональностью, которая обёртывает старую систему, используя шаблоны вроде Strangler, чтобы в итоге можно было избавиться от частей старой системы.

Что проще и быстрее построить: монолит или набор микросервисов? Монолит, всегда. Так что сначала подумайте о создании модульного монолита и рассмотрите микросервисы, когда у вас будет простая рабочая система.

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

Источник: https://ardalis.com/simple-systems-galls-law/
День восемьсот тридцать седьмой. #ЗаметкиНаПолях
Статические Методы - Зло? Начало
Использовать статические методы - хорошо или плохо? Попытаюсь описать эволюцию моего отношения к ним.

1. Ух ты, статические методы!
Впервые узнав о статических методах, большинство людей приходят в восторг. Это понятно, потому что у них есть довольно веские преимущества:
- Они удобны - вы можете вызывать их в любое время, не заботясь ни о каких надоедливых зависимостях.
- Они быстрее - статические методы немного быстрее, чем экземплярные, потому что в экземплярных вы также работаете с неявным параметром this. Исключение этого параметра дает небольшой прирост производительности в большинстве языков программирования.

Учитывая эти 2 преимущества, становится понятно, почему люди поначалу тяготеют к использованию статических методов.

2. Внедрение зависимости.
Через некоторое время вы узнаете о принципах ООП, SOLID и внедрении зависимостей. Вы начинаете понимать, что статические методы вносят некоторые высокоуровневые недостатки в дизайн вашего кода:
- Сильная связанность - код, вызывающий статические методы, тесно связан с вызываемым кодом. Отсутствие промежуточной абстракции затрудняет тестирование этого кода.
- Запутанный график зависимостей - код, заваленный вызовами статических методов, трудно понять, потому что поток зависимостей неявный, и вам нужно прочитать весь класс, чтобы увидеть полную картину.

Явные зависимости (вводимые как параметры конструктора или параметры метода) устраняют эти проблемы. Они позволяют вам вводить абстракции между вызывающим и вызываемым абонентами и, следовательно, использовать модульное тестирование. Они также делают график зависимостей понятным.

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

3. Знакомство с функциональным программированием.
Последнее прозрение приходит, когда вы узнаете о функциональном программировании. Основная идея функционального программирования - избегать скрытых входных и выходных данных, таких как:
- Ссылка на изменяемое состояние,
- Модификация общего состояния (также известная как побочные эффекты),
- Использование исключений для управления ходом программы.

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

Окончание следует…

Источник:
https://enterprisecraftsmanship.com/posts/static-methods-evil/