10 причин, почему macOS лучше Windows для .NET-разработчиков
Visual Studio работает только на Windows. И всё же для большинства .NET-разработчиков macOS может оказаться более удобной рабочей машиной.
Автор больше 13 лет разрабатывает приложения на .NET и на Windows, и на Mac. Вот его сравнение обеих платформ. 10 причин выбрать macOS
1. Unix-подобная система. Пути к файлам, права доступа и shell-скрипты работают ближе к тому, как они устроены на Linux-серверах и в контейнерах, куда вы деплоите приложения.
2. Более удобная работа с Docker. Linux-контейнеры запускаются без типичных проблем WSL, таких как чрезмерное потребление памяти, сетевые сбои и проблемы с синхронизацией файлов.
3. Производительность Apple Silicon. .NET работает нативно на ARM64. Чипы серии M быстро собирают большие проекты и при этом почти не шумят.
4. Автономность на весь день. Можно компилировать код, отлаживать приложения и запускать контейнеры в течение рабочего дня без зарядки.
5. Отличные IDE. JetBrains Rider и VS Code с ReSharper или C# Dev Kit работают на macOS нативно и предоставляют полноценные возможности для разработки.
6. Homebrew. Одна команда позволяет установить .NET SDK, Node.js, базы данных и CLI-инструменты, а затем обновлять их.
7. Качество оборудования. Чёткий экран, удобный трекпад и корректно работающий спящий режим.
8. Безопасность из коробки. macOS обеспечивает высокий уровень безопасности, а приложения должны запрашивать необходимые разрешения на доступ к системным ресурсам.
9. Чистая система. Меньше предустановленного ПО и никакой рекламы в системном интерфейсе.
10. Все цели для .NET MAUI на одном устройстве. Для сборки и подписания приложений под iOS и macOS нужен Mac. На нём можно разрабатывать приложения для Android, iOS и macOS в одном окружении.
5 причин выбрать Windows
1. Полноценная Visual Studio. Расширенные инструменты отладки, визуальные дизайнеры и профилировщики. Visual Studio for Mac закрыли в 2024 году. Если вы привыкли к Visual Studio и не хотите переходить на Rider или VS Code, Windows остаётся основным вариантом.
2. Разработка десктопных приложений. WPF, Windows Forms и WinUI предназначены для Windows и требуют Windows для полноценного запуска и отладки.
3. Старые проекты на .NET Framework. Устаревшие приложения на .NET Framework и ASP.NET могут требовать Windows, особенно если используют специфичные для этой платформы технологии.
4. SQL Server и SSMS. Оба инструмента нативно работают на Windows. На Mac SQL Server можно запустить в Docker, а SQL Server Management Studio (SSMS) не поддерживается нативно.
5. Цена и выбор оборудования. Больше производителей, возможность самостоятельно обновлять оперативную память и накопители, а также более высокая производительность за те же деньги у многих конфигураций.
Большинство новых .NET-приложений работают в Linux-контейнерах. Поэтому Unix-подобная система на ноутбуке может быть ближе к продакшен-окружению, чем Windows.
→ macOS — веб-разработка, облачные сервисы и мобильные приложения.
→ Windows — десктопные приложения и поддержка старых проектов на .NET Framework.
А на чём вы разрабатываете на .NET: macOS, Windows или Linux?
👉 @KodBlog
Visual Studio работает только на Windows. И всё же для большинства .NET-разработчиков macOS может оказаться более удобной рабочей машиной.
Автор больше 13 лет разрабатывает приложения на .NET и на Windows, и на Mac. Вот его сравнение обеих платформ. 10 причин выбрать macOS
1. Unix-подобная система. Пути к файлам, права доступа и shell-скрипты работают ближе к тому, как они устроены на Linux-серверах и в контейнерах, куда вы деплоите приложения.
2. Более удобная работа с Docker. Linux-контейнеры запускаются без типичных проблем WSL, таких как чрезмерное потребление памяти, сетевые сбои и проблемы с синхронизацией файлов.
3. Производительность Apple Silicon. .NET работает нативно на ARM64. Чипы серии M быстро собирают большие проекты и при этом почти не шумят.
4. Автономность на весь день. Можно компилировать код, отлаживать приложения и запускать контейнеры в течение рабочего дня без зарядки.
5. Отличные IDE. JetBrains Rider и VS Code с ReSharper или C# Dev Kit работают на macOS нативно и предоставляют полноценные возможности для разработки.
6. Homebrew. Одна команда позволяет установить .NET SDK, Node.js, базы данных и CLI-инструменты, а затем обновлять их.
7. Качество оборудования. Чёткий экран, удобный трекпад и корректно работающий спящий режим.
8. Безопасность из коробки. macOS обеспечивает высокий уровень безопасности, а приложения должны запрашивать необходимые разрешения на доступ к системным ресурсам.
9. Чистая система. Меньше предустановленного ПО и никакой рекламы в системном интерфейсе.
10. Все цели для .NET MAUI на одном устройстве. Для сборки и подписания приложений под iOS и macOS нужен Mac. На нём можно разрабатывать приложения для Android, iOS и macOS в одном окружении.
5 причин выбрать Windows
1. Полноценная Visual Studio. Расширенные инструменты отладки, визуальные дизайнеры и профилировщики. Visual Studio for Mac закрыли в 2024 году. Если вы привыкли к Visual Studio и не хотите переходить на Rider или VS Code, Windows остаётся основным вариантом.
2. Разработка десктопных приложений. WPF, Windows Forms и WinUI предназначены для Windows и требуют Windows для полноценного запуска и отладки.
3. Старые проекты на .NET Framework. Устаревшие приложения на .NET Framework и ASP.NET могут требовать Windows, особенно если используют специфичные для этой платформы технологии.
4. SQL Server и SSMS. Оба инструмента нативно работают на Windows. На Mac SQL Server можно запустить в Docker, а SQL Server Management Studio (SSMS) не поддерживается нативно.
5. Цена и выбор оборудования. Больше производителей, возможность самостоятельно обновлять оперативную память и накопители, а также более высокая производительность за те же деньги у многих конфигураций.
Большинство новых .NET-приложений работают в Linux-контейнерах. Поэтому Unix-подобная система на ноутбуке может быть ближе к продакшен-окружению, чем Windows.
→ macOS — веб-разработка, облачные сервисы и мобильные приложения.
→ Windows — десктопные приложения и поддержка старых проектов на .NET Framework.
А на чём вы разрабатываете на .NET: macOS, Windows или Linux?
Please open Telegram to view this post
VIEW IN TELEGRAM
1🥴16❤3😁1🌚1🍾1
GitHub ускоряет запись данных в репозитории в 3–4 раза
GitHub меняет механизм блокировок в Spokes: вместо одной блокировки на весь репозиторий будут использоваться отдельные блокировки для каждой ссылки на Git-объект (ref).
Это позволит обновлять разные refs параллельно, не блокируя друг друга, даже при межрегиональных задержках около 25 мс.
По результатам внутренних тестов GitHub, новый подход увеличил пропускную способность операций записи в 3–4 раза.
Главная идея — меньше лишних блокировок и больше параллелизма при работе с репозиториями.
👉 @KodBlog
GitHub меняет механизм блокировок в Spokes: вместо одной блокировки на весь репозиторий будут использоваться отдельные блокировки для каждой ссылки на Git-объект (ref).
Это позволит обновлять разные refs параллельно, не блокируя друг друга, даже при межрегиональных задержках около 25 мс.
По результатам внутренних тестов GitHub, новый подход увеличил пропускную способность операций записи в 3–4 раза.
Главная идея — меньше лишних блокировок и больше параллелизма при работе с репозиториями.
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍5🍾1
Оператор присваивания с проверкой на null
Оператор
Он присваивает правому операнду значение только в том случае, если левый операнд равен
Если переменная уже содержит значение, оно останется без изменений:
Оператор
👉 @KodBlog
??=Оператор
??= появился в C# 8.0 и работает по принципу оператора объединения с null ??.Он присваивает правому операнду значение только в том случае, если левый операнд равен
null.string? name = null;
name ??= "Developer";
Console.WriteLine(name); // Developer
Если переменная уже содержит значение, оно останется без изменений:
string? name = "Alex";
name ??= "Developer";
Console.WriteLine(name); // Alex
Оператор
??= позволяет сократить код и удобно задавать значения по умолчанию, если переменная ещё не инициализирована.Please open Telegram to view this post
VIEW IN TELEGRAM
🙏5❤1🔥1🍾1
Упрощаем работу с коллекциями в C# с помощью Collection Expressions
В C# 12 появились Collection Expressions — выражения коллекций, которые позволяют создавать коллекции с помощью простого и лаконичного синтаксиса
Вместо длинной записи можно использовать квадратные скобки, делая код чище и понятнее.
👉 @KodBlog
В C# 12 появились Collection Expressions — выражения коллекций, которые позволяют создавать коллекции с помощью простого и лаконичного синтаксиса
[].Вместо длинной записи можно использовать квадратные скобки, делая код чище и понятнее.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3👎1👏1🍾1
7 ошибок с async/await, которые допускают даже Senior .NET-разработчики
После нескольких лет работы с production-приложениями на .NET становится понятно: написать асинхронный код несложно. Гораздо сложнее сделать так, чтобы он правильно работал под высокой нагрузкой.
Вот 7 ошибок, которых стоит избегать.
1. Блокировка асинхронного кода через
Как правильно: использовать
2. Использование
Как правильно: возвращать
3. Забывать
Как правильно: использовать
4. Выполнять независимые операции последовательно. Представим, что нужно сделать два независимых API-запроса. При последовательном выполнении второй начнётся только после завершения первого.
Как правильно: запускать обе операции и ожидать их завершения вместе с помощью
5. Игнорировать
Как правильно: принимать
6. Считать, что async означает параллельное выполнение.
Как правильно: использовать
7. Использовать
Как правильно: ограничивать количество одновременно выполняемых операций с помощью
Главное: async/await — это не просто способ повысить производительность. Это инструмент управления ресурсами, параллелизмом, отменой операций и обработкой ошибок.
Метод может быть асинхронным, но при этом замедлять приложение, снижать его надёжность и усложнять отладку.
👉 @KodBlog
После нескольких лет работы с production-приложениями на .NET становится понятно: написать асинхронный код несложно. Гораздо сложнее сделать так, чтобы он правильно работал под высокой нагрузкой.
Вот 7 ошибок, которых стоит избегать.
1. Блокировка асинхронного кода через
.Result или .Wait(). Вызовы .Result и .Wait() блокируют текущий поток в ожидании завершения асинхронной операции. В ASP.NET Core это может привести к исчерпанию потоков Thread Pool (Thread Pool Starvation) и снижению пропускной способности приложения.Как правильно: использовать
await на протяжении всей цепочки вызовов.2. Использование
async void вместо async Task. Метод с async void нельзя ожидать через await, а его исключения нельзя перехватить вызывающим кодом с помощью стандартного механизма обработки исключений на основе Task.Как правильно: возвращать
Task или Task<T>. async void стоит использовать только для обработчиков событий, которым необходим такой формат.3. Забывать
await при вызове асинхронного метода. Если вызвать асинхронный метод без await и не отслеживать возвращённый Task, можно потерять исключения или получить операцию, которая продолжит выполняться за пределами ожидаемого жизненного цикла.Как правильно: использовать
await или явно управлять жизненным циклом задачи.4. Выполнять независимые операции последовательно. Представим, что нужно сделать два независимых API-запроса. При последовательном выполнении второй начнётся только после завершения первого.
Как правильно: запускать обе операции и ожидать их завершения вместе с помощью
Task.WhenAll. Это подходит, если операции действительно независимы и безопасны для параллельного выполнения.5. Игнорировать
CancellationToken. Если не передавать сигнал отмены дальше по цепочке вызовов, ресурсоёмкие запросы к базе данных или HTTP-запросы могут продолжать выполняться даже после отключения клиента.Как правильно: принимать
CancellationToken и передавать его через всю цепочку асинхронных вызовов.6. Считать, что async означает параллельное выполнение.
async/await автоматически не создаёт новые потоки и не ускоряет вычислительно сложные операции. Асинхронность в первую очередь позволяет не блокировать поток во время ожидания.Как правильно: использовать
async для операций ввода-вывода (I/O-bound). Task.Run может подойти для некоторых CPU-bound задач, но его применение в обработчиках серверных запросов требует осторожности.7. Использовать
Task.WhenAll без ограничения параллелизма. Одновременный запуск тысяч запросов к базе данных или HTTP-запросов может исчерпать пулы соединений, перегрузить внешние сервисы и увеличить задержки.Как правильно: ограничивать количество одновременно выполняемых операций с помощью
SemaphoreSlim, Parallel.ForEachAsync или других механизмов управления конкурентностью.Главное: async/await — это не просто способ повысить производительность. Это инструмент управления ресурсами, параллелизмом, отменой операций и обработкой ошибок.
Метод может быть асинхронным, но при этом замедлять приложение, снижать его надёжность и усложнять отладку.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8😐4🍾1
BenchmarkDotNet в .NET Single-File приложениях
Бенчмарки — отличный сценарий использования одnofайловых приложений (Single-File Apps) в .NET.
В примере показано, как использовать BenchmarkDotNet в таком приложении. Самые важные настройки находятся в начале кода, а также стоит обратить внимание на атрибут
Автор делится примером реализации и готов отправить исходный код тем, кто заинтересуется им в комментариях.
👉 @KodBlog
Бенчмарки — отличный сценарий использования одnofайловых приложений (Single-File Apps) в .NET.
В примере показано, как использовать BenchmarkDotNet в таком приложении. Самые важные настройки находятся в начале кода, а также стоит обратить внимание на атрибут
[InProcess].Автор делится примером реализации и готов отправить исходный код тем, кто заинтересуется им в комментариях.
Please open Telegram to view this post
VIEW IN TELEGRAM
🍾4
CQRS без Event Sourcing: используйте сильные стороны EF Core и Dapper
Чтобы получить преимущества CQRS, не обязательно внедрять полноценный Event Sourcing.
Достаточно разделить модели записи и чтения:
• EF Core — для бизнес-правил, доменных агрегатов и операций записи.
• Dapper — для быстрых SQL-запросов и чтения данных.
Такой подход позволяет сохранить бизнес-логику в одном месте, а запросы к данным сделать проще и производительнее.
Прагматично, чисто и без лишней сложности.
👉 @KodBlog
Чтобы получить преимущества CQRS, не обязательно внедрять полноценный Event Sourcing.
Достаточно разделить модели записи и чтения:
• EF Core — для бизнес-правил, доменных агрегатов и операций записи.
• Dapper — для быстрых SQL-запросов и чтения данных.
Такой подход позволяет сохранить бизнес-логику в одном месте, а запросы к данным сделать проще и производительнее.
Прагматично, чисто и без лишней сложности.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🍾2
Message Brokers гарантируют At-Least-Once, а не Exactly-Once
Что произойдёт, если ваш consumer обработает одно и то же событие дважды? Сможет ли система корректно справиться с повторной доставкой?
Брокеры сообщений часто используют семантику доставки At-Least-Once — событие будет доставлено как минимум один раз, но возможны дубликаты.
Поэтому обработчики событий должны быть идемпотентными (Idempotent): повторная обработка одного и того же события не должна приводить к повторным побочным эффектам.
Разберём, как проектировать идемпотентные обработчики событий и защищать систему от дублирующей обработки.
👉 @KodBlog
Что произойдёт, если ваш consumer обработает одно и то же событие дважды? Сможет ли система корректно справиться с повторной доставкой?
Брокеры сообщений часто используют семантику доставки At-Least-Once — событие будет доставлено как минимум один раз, но возможны дубликаты.
Поэтому обработчики событий должны быть идемпотентными (Idempotent): повторная обработка одного и того же события не должна приводить к повторным побочным эффектам.
Разберём, как проектировать идемпотентные обработчики событий и защищать систему от дублирующей обработки.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🍾1
Зачем обрабатывать элементы по одному, если процессор может обрабатывать сразу 8?
В C# есть инструменты для использования SIMD (Single Instruction, Multiple Data) — технологии, которая позволяет выполнять одну операцию одновременно над несколькими элементами данных.
Для этого можно использовать Vector<T> и Hardware Intrinsics.
Что они дают?
Vector<T> — позволяет обрабатывать несколько значений одного типа за одну операцию.
Hardware Intrinsics — дают более низкоуровневый доступ к SIMD-инструкциям процессора, например AVX2 и AVX-512.
SIMD — ускоряет вычисления над массивами чисел, обработку изображений, математические операции и другие задачи с однотипными данными.
И всё это доступно прямо в C# — без необходимости переходить на C++.
👉 @KodBlog
В C# есть инструменты для использования SIMD (Single Instruction, Multiple Data) — технологии, которая позволяет выполнять одну операцию одновременно над несколькими элементами данных.
Для этого можно использовать Vector<T> и Hardware Intrinsics.
Что они дают?
Vector<T> — позволяет обрабатывать несколько значений одного типа за одну операцию.
Hardware Intrinsics — дают более низкоуровневый доступ к SIMD-инструкциям процессора, например AVX2 и AVX-512.
SIMD — ускоряет вычисления над массивами чисел, обработку изображений, математические операции и другие задачи с однотипными данными.
И всё это доступно прямо в C# — без необходимости переходить на C++.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔5👍2🔥2🍾1
Учитывают ли ваши C#-структуры кэш-линии процессора?
Неправильное расположение полей структуры приводит к неэффективному использованию памяти и дополнительным затратам на доступ к кэш-линии процессора.
Используйте
👉 @KodBlog
Неправильное расположение полей структуры приводит к неэффективному использованию памяти и дополнительным затратам на доступ к кэш-линии процессора.
Используйте
[StructLayout(LayoutKind.Sequential, Pack = 1)], чтобы контролировать размещение полей в памяти.Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🤔2👏1🍾1
Каждый раз, когда асинхронный метод возвращает
Если ваш метод часто завершается синхронно (например, при попадании в кеш), используйте
Это поможет избежать миллионов лишних выделений памяти (heap allocations).
👉 @KodBlog
Task<T>, при завершении операции выделяется память в куче.Если ваш метод часто завершается синхронно (например, при попадании в кеш), используйте
ValueTask<T>.Это поможет избежать миллионов лишних выделений памяти (heap allocations).
Please open Telegram to view this post
VIEW IN TELEGRAM
🍾4
Запускаете несколько реплик фонового воркера в Kubernetes?
Локальные блокировки (
Используйте распределённые блокировки Redis (Redis Distributed Locks) с fencing-токенами, чтобы предотвратить состояния гонки (race conditions) и избежать одновременного выполнения одной задачи несколькими воркерами.
👉 @KodBlog
Локальные блокировки (
lock / Monitor) не работают между разными контейнерами.Используйте распределённые блокировки Redis (Redis Distributed Locks) с fencing-токенами, чтобы предотвратить состояния гонки (race conditions) и избежать одновременного выполнения одной задачи несколькими воркерами.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3❤🔥2🍾2
Task.WhenAll vs Parallel.ForEachAsync в .NET: что выбрать?
Оба инструмента позволяют выполнять независимые операции конкурентно, но отличаются способом управления параллельностью и ресурсами.
Правильный выбор зависит от типа нагрузки.
1. Task.WhenAll. Лучше всего подходит для известного набора независимых асинхронных операций, особенно связанных с вводом-выводом (I/O-bound).
• Идеален для нескольких независимых запросов к API или базе данных.
• Не имеет встроенного ограничения количества одновременно выполняемых операций.
• Не создаёт потоки автоматически.
2. Parallel.ForEachAsync. Лучше всего подходит для обработки коллекций с контролируемым уровнем параллельности.
• Поддерживает I/O-bound и CPU-bound нагрузки.
• Имеет встроенный механизм ограничения параллельности.
• Полезен при обработке больших коллекций и выполнении внешних API-запросов.
3. Что выбрать?
• CPU-bound: предпочитайте
• I/O-bound: используйте
Внешние API: подходят оба варианта, но
Важное напоминание об EF Core:
Никогда не выполняйте параллельные запросы через один и тот же экземпляр
Главное:
•
•
И помните: по умолчанию
Какой из этих инструментов вы чаще используете в production-приложениях на .NET?
👉 @KodBlog
Оба инструмента позволяют выполнять независимые операции конкурентно, но отличаются способом управления параллельностью и ресурсами.
Правильный выбор зависит от типа нагрузки.
1. Task.WhenAll. Лучше всего подходит для известного набора независимых асинхронных операций, особенно связанных с вводом-выводом (I/O-bound).
var usersTask = GetUsersAsync();
var ordersTask = GetOrdersAsync();
await Task.WhenAll(usersTask, ordersTask);
• Идеален для нескольких независимых запросов к API или базе данных.
• Не имеет встроенного ограничения количества одновременно выполняемых операций.
• Не создаёт потоки автоматически.
2. Parallel.ForEachAsync. Лучше всего подходит для обработки коллекций с контролируемым уровнем параллельности.
await Parallel.ForEachAsync(
orders,
new ParallelOptions
{
MaxDegreeOfParallelism = 10
},
async (order, ct) =>
await ProcessOrderAsync(order, ct));
• Поддерживает I/O-bound и CPU-bound нагрузки.
• Имеет встроенный механизм ограничения параллельности.
• Полезен при обработке больших коллекций и выполнении внешних API-запросов.
3. Что выбрать?
• CPU-bound: предпочитайте
Parallel.ForEach для синхронных операций с высокой вычислительной нагрузкой.• I/O-bound: используйте
Task.WhenAll для нескольких независимых операций или Parallel.ForEachAsync, если необходимо ограничить параллельность.Внешние API: подходят оба варианта, но
Parallel.ForEachAsync часто удобнее для обработки сотен запросов без чрезмерной нагрузки на внешние сервисы.Важное напоминание об EF Core:
DbContext не является потокобезопасным (thread-safe).Никогда не выполняйте параллельные запросы через один и тот же экземпляр
DbContext. Используйте отдельные экземпляры или выполняйте запросы последовательно.Главное:
•
Task.WhenAll ожидает завершения нескольких задач без встроенного ограничения параллельности.•
Parallel.ForEachAsync обрабатывает коллекции с ограниченным уровнем параллельности.И помните: по умолчанию
Parallel.ForEachAsync допускает количество одновременно выполняемых операций, равное Environment.ProcessorCount. Настраивайте MaxDegreeOfParallelism с учётом особенностей вашей нагрузки.Какой из этих инструментов вы чаще используете в production-приложениях на .NET?
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🍾1
CQRS ≠ MediatR.
Перестаньте смешивать CQRS и MediatR.
То, что вы используете MediatR, не означает, что у вас реализован CQRS.
CQRS — это про разделение операций чтения и записи, часто с разными моделями, стратегиями хранения и иногда с разными хранилищами данных.
MediatR — это библиотека для диспетчеризации сообщений внутри процесса. Она удобна для организации команд и запросов, но это не фреймворк CQRS.
Можно использовать MediatR в архитектуре, построенной по CQRS.
Но не стоит считать, что одно автоматически означает другое.
Сначала разберитесь в различии, прежде чем внедрять паттерн или инструмент.
👉 @KodBlog
Перестаньте смешивать CQRS и MediatR.
То, что вы используете MediatR, не означает, что у вас реализован CQRS.
CQRS — это про разделение операций чтения и записи, часто с разными моделями, стратегиями хранения и иногда с разными хранилищами данных.
MediatR — это библиотека для диспетчеризации сообщений внутри процесса. Она удобна для организации команд и запросов, но это не фреймворк CQRS.
Можно использовать MediatR в архитектуре, построенной по CQRS.
Но не стоит считать, что одно автоматически означает другое.
Сначала разберитесь в различии, прежде чем внедрять паттерн или инструмент.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7👏1🍾1
Бесплатный декомпилятор .NET
Инструмент позволяет декомпилировать приложения в C#, восстанавливать целые проекты, искать методы и классы, изучать иерархию типов и просматривать метаданные.
Также доступен в виде расширений для Visual Studio и VS Code, а для работы из терминала есть CLI-версия.
https://github.com/icsharpcode/ILSpy
👉 @KodBlog
Инструмент позволяет декомпилировать приложения в C#, восстанавливать целые проекты, искать методы и классы, изучать иерархию типов и просматривать метаданные.
Также доступен в виде расширений для Visual Studio и VS Code, а для работы из терминала есть CLI-версия.
https://github.com/icsharpcode/ILSpy
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3