Не попадитесь на накрученные каналы! Узнайте, не накручивает ли канал просмотры или подписчиков Проверить канал на накрутку
Прикрепить Телеграм-аккаунт Прикрепить Телеграм-аккаунт

Телеграм канал «Отдел разработки»

Отдел разработки
45
0
2
2
0
Заметки о разработке от команды онлайн школы программирования IRON PROGRAMMER
Подписчики
Всего
59
Сегодня
0
Просмотров на пост
Всего
22
ER
Общий
36.72%
Суточный
23.73%
Динамика публикаций
Telemetr - сервис глубокой аналитики
телеграм-каналов
Получите подробную информацию о каждом канале
Отберите самые эффективные каналы для
рекламных размещений, по приросту подписчиков,
ER, количеству просмотров на пост и другим метрикам
Анализируйте рекламные посты
и креативы
Узнайте какие посты лучше сработали,
а какие хуже, даже если их давно удалили
Оценивайте эффективность тематики и контента
Узнайте, какую тематику лучше не рекламировать
на канале, а какая зайдет на ура
Попробовать бесплатно
Показано 7 из 45 постов
Смотреть все посты
Пост от 24.10.2026 22:13
9
1
0
Немного про CLI
Материал про CLI приложения выходит долго и мы еще не дошли до конца, хотя и большую часть уже рассмотрели. И вот когда имеется понимание, начинаешь видеть места применения: где и для чего может подойти такое приложение (про CLI речь).

В контексте последних дней, когда работа над маппером идёт полным ходом и с некоторой периодичностью запускаются бенчмарки в разных сочетаниях и вариантах, а я напомню, что они запускаются из PowerShell скриптов, вполне разумным посчитал написать CLI приложение для запуска этих самых бенчмарков, с интерактивным режимом, прогрессбарами разными режимами вывода. Одну большую задачу такое приложение решает - мало кто захочет разбираться в устройстве скриптов, чтобы понять, какие там есть флаги для запуска.
Мало кто в принципе захочет запускать проверки малоизвестной библиотеки и сравнивать числа в README с фактическими.
Но тем не менее, однажды такой смельчак найдется и у него (теперь) есть такой инструмент!
Перечислять все возможности приложения (а оно не имеет отдельного названия, потому что вне репозитория не имеет никакого применения) смысла не имеет, но главное: оно может в интерактивном режиме дать выбрать, что именно надо запустить, какую сборку использовать, какую версию .net подставить и потом красиво все отобразить в терминале.

Но есть и режим, когда передаются флаги и параметры на этапе запуска - для случаев, когда нужно более тонко управлять процессом.
Вот как это выглядит в жизни:
Comparison.CLI in action ))
И вишенка на торте: приложение запускается из еще одного скрипта! )))
Ты должен был бороться со злом, а не примкнуть к нему!
Тут на самом деле все просто. Размещать внутри репозитория собранное приложение неправильно, а изменения в него могут потребоваться после каждой правки скриптов или создания нового. Поэтому проще запустить его из другого скрипта - он соберет приложение и запустит его, а дальше пользоваться его возможностями. Пока так. Думаю в процессе придет какое-то более красивое решение и не надо будет городить велосипед. Например вынести всю логику бенчмарков внутрь приложения и не пользоваться скриптами вовсе.
———
Пара слов про вывод на скриншоте.
Команда, которая запускает приложение - ./bench.ps1
В этом скрипте всего 31 строка из которых половина это синопсис и комментарии. Его задача собрать приложение и запустить, а если что-то пошло не так, то перенаправить в вывод код завершения ($LASTEXITCODE - мы про него говорили в серии про CLI приложения).
Просто вызов скрипта включает интерактивный режим, а если передать аргументы, то скрипт так же перенаправляет их в приложение, где они считываются и обрабатываются.
Пример из скриншота:
Запуск прогона сравнения текущей ветки в двух вариантах .net (.net 8 участвует в этом из-за того, что потенциально могут быть проекты на нем написанные, в которых может быть использован маппер):

# команда, запускающая CLI приложение и аргументы
./bench.ps1 quick --filter 'InPlace,Flat,Nested,Collection' --frameworks 'net10.0,net8.0' --branch perf/aggressive-inlining-public-methods

# команда для аналогичного запуска скрипта напрямую
pwsh -NoProfile -NonInteractive -File ./scripts/run-quick.ps1 -Filter '.InPlaceBenchmark.,.FlatBenchmark.,.NestedBenchmark.,.CollectionBenchmark.' -Frameworks 'net10.0,net8.0' -Branch perf/aggressive-inlining-public-methods


Не такая большая разница, но нюансы есть. Например, cli удерживает систему от перехода в спящий режим. Для бенчмарка это важно - система должна находиться в одинаковом состоянии на протяжении всего прогона. А еще очень не хочется потратить несколько часов впустую, если процесс остановится на полпути из-за того, что все уснули и комп уснул ))
Быстренько по составу команды:
./bench.ps1 - скрипт запуска
quick - быстрый режим. Их всего два, второй - полный, но на промежуточном коммите нет смысла в полном
--filter 'InPlace,Flat,Nested,Collection' - фильтр проверок: что именно проверяется.
--frameworks 'net10.0,net8.0' очевидно из текста ))
--branch perf/aggressive-inlining-public-methods тоже очевидно - указываем конкретную ветку
Прямой запуск чуть более многословный, хотя принцип тот же самый.
——
Захотите попробовать - всё здесь
gh repo clone iron-mapper/IronMapper
Запуск скрипта из папки /benchmarks там же код cli.
Изображение
Пост от 23.10.2026 14:27
2
0
0
Вышел релиз IronMapper 2.0.0 !
Результаты замеров
Маленький шаг для человечества, но большой для команды
С этой фразы начался пост о первом релизе библиотеки IronMapper. На тот момент идея была в том, чтобы заменить AutoMapper, который сменил тип лицензии и внедрил необходимость получать лицензию на использование. А еще хотелось сделать лучше. И конечно применить в текущих проектах. Время шло, применить так и не успел, а работу над библиотекой приостановил — и применять было некуда и куча других дел появилось.
Из Readme:
"Маппер объектов для .NET, работающий во время компиляции: генератор исходного кода (Roslyn source generator) пишет код маппинга на обычном C# при сборке проекта."

Прошло время, в текущем проекте понадобилось подключить маппер и тут началось!
Когда пишешь библиотеку, без реального применения тесты не дают полной гарантии, что всё будет работать как надо. Появилось место применения и сразу нашлось, куда приложить руки.
По итогу, за минувшие несколько недель выпущено несколько версий, каждая из которых исправляла баги и недочеты, а заодно улучшала показатели в сравнении с текущими основными мапперами, широко известными и применяемыми во множестве проектов. Я говорю не только про AutoMapper, но и Mapster и Mapperly.
Итак, как всё развивалось в последние несколько недель:
1.1.2 — подключил в свой проект и сразу нашёл баги
• Маппинг internal-классов не собирался: генератор делал все методы публичными. Теперь доступность методов подстраивается под типы.
• Лямбды в профилях копировались как текст, поэтому статические методы профиля и using из его файла «терялись». Теперь генератор разбирает лямбды через семантическую модель, и всё это работает.
1.1.3 — решения из нескольких проектов
• Два UserDto в разных пространствах имён больше не ломают сборку: методы получают уточнённые имена, а генератор подсказывает, как их вызывать.
• Если одну и ту же пару типов случайно описать дважды, генератор больше не роняет генерацию для всего проекта. Он выдаёт понятное предупреждение и продолжает работу.
2.0.0 — скорость, NativeAOT и удобство • IMapper из DI переписан без рефлексии: работает в NativeAOT, не создаёт лишних аллокаций и возвращает исходные исключения, а не TargetInvocationException. Вызов — 15 нс, у AutoMapper — 34,5.
• Коллекции теперь маппятся циклами вместо LINQ. Приятный побочный эффект: прирост к NativeAOT-бинарнику сократился с 255 КБ до 2,6 КБ. Это меньше, чем у Mapperly ))

• using IronMapper.Generated писать больше не нужно, он подключается сам. • Ошибки маппинга в IDE теперь указывают на конкретную строку, а быстрые исправления (лампочка) наконец работают.
• Ненужные MapToXList и MapToXArray можно отключить, чтобы не портили покрытие кода тестами.
• У сгенерированного класса теперь своё имя в каждой сборке. Это и починило мой архитектурный тест: ArchUnitNET склеивал одноимённые классы из разных проектов.
После 1.1.3 планировал поднять версию до 1.2.0, но я проверил его на своём проекте, и он уронил архитектурный тест. Пока разбирался, насчитал пять изменений, которые могут сломать чужой код. Так что пришлось поднять мажорную версию ))
Для обновления есть отдельное руководство: что сломается и что сделать.
Отдельно добавил проект с бенчмарками, которые проводят замеры по размеру сборок и скорости работы, в т.ч. холодного старта. Всё это есть в подробностях в репозитории.
Работа идёт и ещё много всего надо реализовать, однако совершенно точно можно сказать, что если чего-то захотеть, можно сделать ничуть не хуже имеющихся признанных продуктов.
Если попробуете — расскажите, что сломалось. Самые полезные баги пока находит реальный код 🐈
Изображение
Пост от 21.10.2026 11:45
13
0
0
PowerShell сломался


Открываю новое окно, а там:
PowerShell 7.6.6
Process terminated.
totalWidth ('-11') must be a non-negative value. (Parameter 'totalWidth')
Actual value was -11.
at System.Environment.FailFast(System.Runtime.CompilerServices.StackCrawlMarkHandle, System.String, System.Runtime.CompilerServices.ObjectHandleOnStack, System.String)
at System.Environment.FailFast(System.Threading.StackCrawlMark ByRef, System.String, System.Exception, System.String)
at System.Environment.FailFast(System.String, System.Exception)
at Microsoft.PowerShell.UnmanagedPSEntry.Start(System.String[], Int32)
at Microsoft.PowerShell.ManagedPSEntry.Main(System.String[])
System.ArgumentOutOfRangeException: totalWidth ('-11') must be a non-negative value. (Parameter 'totalWidth')
Actual value was -11.
at System.ArgumentOutOfRangeException.ThrowNegative[T](T value, String paramName)
at System.String.PadRight(Int32 totalWidth, Char paddingChar)
at Microsoft.PowerShell.UpdatesNotification.ShowUpdateNotification(PSHostUserInterface hostUI)
at Microsoft.PowerShell.ConsoleHost.Start(String bannerText, String helpText, Boolean issProvidedExternally)
at Microsoft.PowerShell.UnmanagedPSEntry.Start(String[] args, Int32 argc)
Приплыли.. И сколько не перезапускай, всё одно. Грустно и непонятно.

Оказалось, проблема проявилась у многих и она имеет быстрое решение:

1. Нажимаем Win + R
Вводим cmd
В открывшейся консоли пишем setx POWERSHELL_UPDATECHECK Off
Закрываем, открываем PowerShell и всё работает!
По сути это временная мера - мы записываем настройку с отключением проверки обновления в переменную окружения. Как только Майки починят (а в версии 7.6.7 проблема не ушла, как пишут в сети) можно будет значение поменять обратно.
Have a nice day ))
Пост от 18.10.2026 10:20
6
0
0
CLI приложения #18. Конвейеры и стандартный ввод

В шестом посте мы разбирали два потока вывода — stdout и stderr. Пора рассказать про третий, входящий. Называется он стандартным вводом, stdin, и номер у него нулевой.
Обычно stdin — это клавиатура: Console.ReadLine() читает то, что набрал человек. Но точно так же, как вывод можно перенаправить в файл, ввод можно перенаправить из файла. Или из другой программы.
Вот тут и появляется вертикальная черта:
program1 | program2
Эта конструкция называется конвейером. Читается просто: всё, что первая программа напишет в stdout, попадёт во вторую на stdin. Выход одного становится входом другого, напрямую, без промежуточных файлов.
Именно на этом принципе построен весь консольный мир. Каждая утилита делает одну вещь, а сложные задачи собираются из них, как из кубиков. И чтобы ваше приложение можно было использовать в таком конвейере, оно должно уметь читать stdin.
Читается он просто:
// построчно, пока данные не кончатся
string? line;
while ((line = Console.In.ReadLine()) != null)
{
Console.Out.WriteLine(line.ToUpper());
}
Console.In — это тот же поток, что читает Console.ReadLine(), только в виде объекта TextReader. Когда данные кончаются, метод возвращает null. Если ввод пришёл из файла — на последней строке файла. Если с клавиатуры — когда пользователь нажмёт Ctrl+D в Unix или Ctrl+Z и Enter в Windows.
Теперь как это принято оформлять. В мире CLI есть договорённость: дефис вместо имени файла означает стандартный ввод. Её поддерживают десятки утилит, и пользователи её знают.
Соберём команду, которая считает строки — хоть в файле, хоть в потоке:
using System.CommandLine;
using System.Text;

internal class Program
{
private static int Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;

var pathArgument = new Argument<string>("path")
{
Description = "Файл или - для чтения из стандартного ввода",
DefaultValueFactory = _ => "-"
};

var countCommand = new Command("count", "Посчитать строки и слова");
countCommand.Arguments.Add(pathArgument);

countCommand.SetAction(parseResult =>
{
var path = parseResult.GetValue(pathArgument)!;

if (path == "-" && !Console.IsInputRedirected)
{
Console.Error.WriteLine(
"Нет данных на входе. Укажите файл или передайте данные через конвейер");
return 1;
}

if (path != "-" && !File.Exists(path))
{
Console.Error.WriteLine($"Файл не найден: {path}");
return 2;
}

using TextReader reader = path == "-"
? Console.In
: new StreamReader(path);

var lines = 0;
var words = 0;

string? line;
while ((line = reader.ReadLine()) != null)
{
lines++;
words += line.Split(
(char[]?)null,
StringSplitOptions.RemoveEmptyEntries).Length;
}

if (Console.IsOutputRedirected)
Console.Out.WriteLine($"{lines} {words}");
else
Console.Out.WriteLine($"Строк: {lines}, слов: {words}");
return 0;
});

var rootCommand = new RootCommand("Учебное CLI приложение");
rootCommand.Subcommands.Add(countCommand);

return rootCommand.Parse(args).Invoke();
}
}
Ключевая строка здесь — выбор источника. Дальше код работает с TextReader и вообще не знает, откуда пришли данные: из файла, из конвейера или с клавиатуры.

Проверяем все три способа:
# из файла (добавьте файл в текущее расположение)
dotnet run -- count data.txt

# из конвейера
Get-Content data.txt | dotnet run -- count -

# прямо из строки
"первая строка`nвторая строка" | dotnet run -- count -
Важно!
Обратите внимание на проверку Console.IsInputRedirected. Без неё программа, запущенная как myapp count - без данных на входе, молча зависнет в ожидании клавиатуры. Пользователь решит, что она повисла, и будет прав.
Ещё один момент — чтение целиком. Соблазнительно написать так:
var text = Console.In.ReadToEnd();
Это нормально для небольших данных, но означает, что всё содержимое окажется в памяти. А через конвейер вам могут подать лог на несколько гигабайт. Построчное чтение из примера выше работает потоково: обработали строку — забыли про неё.
И отдельная засада для Windows. PowerShell передаёт данные внешним программам в своей кодировке, которая по умолчанию может не совпадать с UTF-8 — и русский текст приедет в виде вопросительных знаков. Лечится явной настройкой (пост)
В самом приложении симметрично задаём кодировку чтения:
Console.InputEncoding = Encoding.UTF8;
Поддержка stdin — это то, что превращает вашу утилиту из "вещи в себе" в деталь конструктора. Пользователи придумают такие связки, которые вам и в голову не приходили, и ничего дописывать для этого не придётся.
Пост от 18.10.2026 10:20
6
0
0
Видео/гифка
Пост от 17.10.2026 12:09
8
0
0
Пост от 17.10.2026 12:09
8
0
0
Commit message по-человечески

Субботнее полезное: как написать осознанное многострочное сообщение к коммиту удобно и наглядно знаете? Сейчас расскажу!
Когда нужно написать большое сопровождение в commit message, в PowerShell это делать неудобно, а если вставлять из буфера, может получиться непредсказуемо и вообще не то, что хочется.
Решение простое. Пишем:
git config --global core.editor "code --wait"
а затем
git commit
Откроется Visual Studio Code с перечислением изменений. Пишем под ними сообщение к коммиту, сохраняем и закрываем вкладку. Всё, большое многострочное сообщение добавлено к коммиту!
Можно выбрать любой текстовый редактор. Примеры в карточке ниже.
Смотреть все посты