Не выкатывай приложение, работающее с Kafka, в прод, без нагрузочного тестирования
Если твой Kafka-пайплайн не прогонялся под реальной нагрузкой, мелкие баги очень быстро превратятся в крупные на проде.
А такие проблемы с производительностью в продакшене обходятся дорого, и по деньгам, и по репутации.
Kafka-пайплайн может работать идеально в девелоперской среде, где трафик минимальный.
Но в проде неожиданные пики нагрузки легко вскрывают скрытые проблемы:
• проблемы с конкурентным доступом;
• неверные настройки для обработки backpressure;
• узкие места в I/O или сериализации при обработке сообщений;
• неправильное партиционирование, из-за которого нагрузка на консьюмеров распределяется неравномерно;
• неверно рассчитанное масштабирование числа консьюмеров под ожидаемую нагрузку;
• некорректное использование Kafka-транзакций, требующих координации;
• задержки commit-ack от нод.
В зависимости от твоего кейса, объёма данных, формата сообщений, сетевых условий, железа, конфигурации и качества кода, при работе с Kafka могут возникать разные проблемы:
• потеря или дублирование сообщений;
• перегрузка или падение брокеров;
• уязвимости или нарушения требований безопасности и комплаенса.
Для многих компаний скорость Kafka-пайплайна критична - особенно если она влияет на SLA.
Когда задаются SLO или SLA, латентность конкретных потоков должна оставаться стабильной, что делает задачу по обеспечению скорости и эффективности пайплайна ещё сложнее.
NBomber - мощный инструмент для симуляции реальной нагрузки на Kafka в .NET-приложениях.
Он позволяет тестировать продюсеров, консьюмеров и end-to-end флоу с помощью сценариев, написанных на C# или F#.
С NBomber можно:
• измерять полную латентность между продюсером и консьюмером;
• отслеживать, как меняется throughput при росте нагрузки;
• определять момент, когда консьюмер начинает отставать от продюсера.
Я использовал NBomber для нагрузочного тестирования боевого Kafka-пайплайна системы Fraud Detection, построенной на микросервисах.
👉 @KodBlog
Если твой Kafka-пайплайн не прогонялся под реальной нагрузкой, мелкие баги очень быстро превратятся в крупные на проде.
А такие проблемы с производительностью в продакшене обходятся дорого, и по деньгам, и по репутации.
Kafka-пайплайн может работать идеально в девелоперской среде, где трафик минимальный.
Но в проде неожиданные пики нагрузки легко вскрывают скрытые проблемы:
• проблемы с конкурентным доступом;
• неверные настройки для обработки backpressure;
• узкие места в I/O или сериализации при обработке сообщений;
• неправильное партиционирование, из-за которого нагрузка на консьюмеров распределяется неравномерно;
• неверно рассчитанное масштабирование числа консьюмеров под ожидаемую нагрузку;
• некорректное использование Kafka-транзакций, требующих координации;
• задержки commit-ack от нод.
В зависимости от твоего кейса, объёма данных, формата сообщений, сетевых условий, железа, конфигурации и качества кода, при работе с Kafka могут возникать разные проблемы:
• потеря или дублирование сообщений;
• перегрузка или падение брокеров;
• уязвимости или нарушения требований безопасности и комплаенса.
Для многих компаний скорость Kafka-пайплайна критична - особенно если она влияет на SLA.
Когда задаются SLO или SLA, латентность конкретных потоков должна оставаться стабильной, что делает задачу по обеспечению скорости и эффективности пайплайна ещё сложнее.
NBomber - мощный инструмент для симуляции реальной нагрузки на Kafka в .NET-приложениях.
Он позволяет тестировать продюсеров, консьюмеров и end-to-end флоу с помощью сценариев, написанных на C# или F#.
С NBomber можно:
• измерять полную латентность между продюсером и консьюмером;
• отслеживать, как меняется throughput при росте нагрузки;
• определять момент, когда консьюмер начинает отставать от продюсера.
Я использовал NBomber для нагрузочного тестирования боевого Kafka-пайплайна системы Fraud Detection, построенной на микросервисах.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Я как кодер довольно щепетилен в разнице между цифрой 0 и буквой O.
В конце созвона саппорт продиктовал мне номер тикета:
четыре, один, восемь, T, A, два, «ОУ»
Зная, что обычные люди вечно путают, я уточнил:
«Это ноль, цифра? Или O, буква?»
(долгая пауза)
«Это ноль, буква.»
👉 @KodBlog
В конце созвона саппорт продиктовал мне номер тикета:
четыре, один, восемь, T, A, два, «ОУ»
Зная, что обычные люди вечно путают, я уточнил:
«Это ноль, цифра? Или O, буква?»
(долгая пауза)
«Это ноль, буква.»
Please open Telegram to view this post
VIEW IN TELEGRAM
😁36🤯25🔥2
Разбор самой жёсткой уязвимости в .NET. Request smuggling и CVE-2025-55315
https://andrewlock.net/understanding-the-worst-dotnet-vulnerability-request-smuggling-and-cve-2025-55315/
В статье объяснили, что такое request smuggling, разбирают свежую дыру в AspNetCore с критичностью 9.9 и показывают, как атакеры могут это дело использовать.
👉 @KodBlog
https://andrewlock.net/understanding-the-worst-dotnet-vulnerability-request-smuggling-and-cve-2025-55315/
В статье объяснили, что такое request smuggling, разбирают свежую дыру в AspNetCore с критичностью 9.9 и показывают, как атакеры могут это дело использовать.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
image_2025-10-29_09-13-40.png
2 MB
Проект на выходных: змейка на C#
Код получился кривоватый. Уверен, есть тысяча способов сделать это лучше. Я просто пытался сам допереть, как всё работает.
Немного олдскульной ностальгии по Nokia.
👉 @KodBlog
Код получился кривоватый. Уверен, есть тысяча способов сделать это лучше. Я просто пытался сам допереть, как всё работает.
Немного олдскульной ностальгии по Nokia.
Please open Telegram to view this post
VIEW IN TELEGRAM
👏14🥴7❤2👍2
Media is too big
VIEW IN TELEGRAM
Только что вышел Minecraftonia — воксельный движок на C# 13/.NET 9 и Avalonia. В проекте экспериментируют с кастомным воксельным рейтрейсингом, процедурной генерацией террейна и отзывчивым десктопным интерфейсом, при этом всё полностью кроссплатформенно.
Ссылка на релиз: https://github.com/wieslawsoltes/Minecraftonia/releases/tag/v0.1.0
👉 @KodBlog
Ссылка на релиз: https://github.com/wieslawsoltes/Minecraftonia/releases/tag/v0.1.0
Please open Telegram to view this post
VIEW IN TELEGRAM
😁15🔥14🥴3
В Entity Framework Core ToDictionaryAsync (и синхронный ToDictionary) вытягивают из базы весь объект целиком.
Если глянуть на сигнатуру ToDictionaryAsync:
Аргументы тут обычные лямбды (Func<TSource, ...>), а не выражения (Expression<Func<...>>). Значит, вычисление этих селекторов происходит на клиенте.
Допустим, у нас есть такой класс:
И вот такой запрос:
Звучит красиво, но по факту EF притащит не только Author и Title, а вообще все поля, например, и Content тоже. На это есть issue, и я случайно наткнулся и офигел.
Как обойти проблему: сначала спроецировать данные через Select, это уже выполнится на стороне сервера:
👉 @KodBlog
Если глянуть на сигнатуру ToDictionaryAsync:
public static Task<Dictionary<TKey, TElement>> ToDictionaryAsync<TSource, TKey, TElement>(
this IQueryable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
CancellationToken cancellationToken = default)
where TKey : notnull
=> ToDictionaryAsync(source, keySelector, elementSelector, comparer: null, cancellationToken);
Аргументы тут обычные лямбды (Func<TSource, ...>), а не выражения (Expression<Func<...>>). Значит, вычисление этих селекторов происходит на клиенте.
Допустим, у нас есть такой класс:
public class BlogPost
{
public int Id { get; set; }
public required string Title { get; set; }
public required string Description { get; set; }
public required string Content { get; set; }
public required string Author { get; set; }
}
И вот такой запрос:
return await dbContext.BlogPosts
.Where(...)
.ToDictionaryAsync(k => k.Author, v => v.Title);
Звучит красиво, но по факту EF притащит не только Author и Title, а вообще все поля, например, и Content тоже. На это есть issue, и я случайно наткнулся и офигел.
Как обойти проблему: сначала спроецировать данные через Select, это уже выполнится на стороне сервера:
return await dbContext.BlogPosts
.Where(...)
.Select(s => new { Author = s.Author, Title = s.Title })
.ToDictionaryAsync(k => k.Author, v => v.Title);
Please open Telegram to view this post
VIEW IN TELEGRAM
❤17🔥7🤔2
Мне очень нравится, что в C# 14 убрали лишнюю возню, связанную с полями, лежащими в основе свойств.
Особенно круто для таких штук, как INotifyPropertyChanged, потому что больше не нужен отдельный приватный field только ради вызова уведомления.
Ссылка: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#the-field-keyword
👉 @KodBlog
Особенно круто для таких штук, как INotifyPropertyChanged, потому что больше не нужен отдельный приватный field только ради вызова уведомления.
Ссылка: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#the-field-keyword
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15🔥12🥰2👍1
Перемещение Файлов и Папок в Корзину в .NET
При работе с файлами и папками в .NET-приложениях на Windows иногда нужно отправлять объекты в корзину, а не удалять их насовсем (File.Delete, Directory.Delete). Это позволяет пользователю восстановить случайно удалённые данные. Вот как сделать это через Windows Shell API:
Этот метод подходит и для файлов, и для каталогов:
👉 @KodBlog
При работе с файлами и папками в .NET-приложениях на Windows иногда нужно отправлять объекты в корзину, а не удалять их насовсем (File.Delete, Directory.Delete). Это позволяет пользователю восстановить случайно удалённые данные. Вот как сделать это через Windows Shell API:
static void MoveToRecycleBin(string path)
{
var shellType = Type.GetTypeFromProgID("Shell.Application", throwOnError: true)!;
dynamic shellApp = Activator.CreateInstance(shellType)!;
// https://learn.microsoft.com/en-us/windows/win32/api/shldisp/ne-shldisp-shellspecialfolderconstants?WT.mc_id=DT-MVP-5003978
var recycleBin = shellApp.Namespace(0xa);
// https://learn.microsoft.com/en-us/windows/win32/shell/folder-movehere?WT.mc_id=DT-MVP-5003978
recycleBin.MoveHere(path);
}
Этот метод подходит и для файлов, и для каталогов:
MoveToRecycleBin(@"C:\path\to\file.txt");
MoveToRecycleBin(@"C:\path\to\directory");
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤3🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
От чат‑бота к агентам: Microsoft Agent Framework в .NET
Microsoft опубликовала руководство по переходу с обычных чат-ботов на AI-агентов в .NET. В примерах показывают, как подключить Microsoft Agent Framework к AI-шаблонам, зарегистрировать агента в Program.cs, вынести поиск в инструмент через
Есть упоминание расширений, мультиягентов, кастомного middleware и развёртывания в Azure с рабочими примерами. Агент уже не болтает, а самостоятельно решает задачи.
👉 @KodBlog
Microsoft опубликовала руководство по переходу с обычных чат-ботов на AI-агентов в .NET. В примерах показывают, как подключить Microsoft Agent Framework к AI-шаблонам, зарегистрировать агента в Program.cs, вынести поиск в инструмент через
SearchAsync, настроить Azure OpenAI и векторное хранилище, а также включить телеметрию через OpenTelemetry и тестирование в .NET Aspire. Есть упоминание расширений, мультиягентов, кастомного middleware и развёртывания в Azure с рабочими примерами. Агент уже не болтает, а самостоятельно решает задачи.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🥴6🤨2👍1
Это может быть первым шагом при написании юнит-тестов.
Не всегда понятно, с чего начать. Но шаблон AAA почти всегда выручает.
// act ... код, который тестируем
// ДОЛЖЕН БЫТЬ ОДИН КОНКРЕТНЫЙ ВЫЗОВ
// arrange ... моки и подготовка окружения для act
// assert ... проверки результата act
И вот — у вас уже есть юнит-тест.
Дальше можно:
a) дублировать Test01 и менять под следующий кейс
b) параметризовать Test01, чтобы прогонять сразу несколько вариантов
В конце просто переименовать Test01..TestN во что-то осмысленное.
👉 @KodBlog
Не всегда понятно, с чего начать. Но шаблон AAA почти всегда выручает.
// act ... код, который тестируем
// ДОЛЖЕН БЫТЬ ОДИН КОНКРЕТНЫЙ ВЫЗОВ
// arrange ... моки и подготовка окружения для act
// assert ... проверки результата act
И вот — у вас уже есть юнит-тест.
Дальше можно:
a) дублировать Test01 и менять под следующий кейс
b) параметризовать Test01, чтобы прогонять сразу несколько вариантов
В конце просто переименовать Test01..TestN во что-то осмысленное.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15
Requestum — новая быстрая и бесплатная альтернатива MediatR
После новостей о коммерциализации MediatR разработчики начали искать замену. Автор представил Requestum — open source-медиатор под MIT-лицензией, с упором на производительность и простоту миграции.
Библиотека явно разделяет команды, запросы и события, использует отдельные Execute/Handle/Publish, поддерживает sync и async middleware, а также множественных подписчиков. Интеграция с DI минимальная, можно подменить MediatR почти без правок кода.
Бенчмарки показывают ускорение на 20–50% и снижение аллокаций на 30–60%. API прозрачный, CQRS-подход сохраняется, а синхронные обработчики работают реально синхронно.
Requestum уже доступен на NuGet, исходники на GitHub. Хороший вариант для тех, кто не хочет зависеть от лицензий и хочет выжать максимум из .NET-медиатора.
Читать подробнее: https://habr.com/ru/articles/961936/
👉 @KodBlog
После новостей о коммерциализации MediatR разработчики начали искать замену. Автор представил Requestum — open source-медиатор под MIT-лицензией, с упором на производительность и простоту миграции.
Библиотека явно разделяет команды, запросы и события, использует отдельные Execute/Handle/Publish, поддерживает sync и async middleware, а также множественных подписчиков. Интеграция с DI минимальная, можно подменить MediatR почти без правок кода.
Бенчмарки показывают ускорение на 20–50% и снижение аллокаций на 30–60%. API прозрачный, CQRS-подход сохраняется, а синхронные обработчики работают реально синхронно.
Requestum уже доступен на NuGet, исходники на GitHub. Хороший вариант для тех, кто не хочет зависеть от лицензий и хочет выжать максимум из .NET-медиатора.
Читать подробнее: https://habr.com/ru/articles/961936/
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👨💻1
Совет по Visual Studio:
Когда создаешь метод, который возвращает JSON через raw string literal, добавь
/* lang=json*/
Этот момент нигде не задокументирован. Если в JSON будет синтаксическая ошибка вроде пропущенной запятой, Visual Studio подсветит её.
👉 @KodBlog
Когда создаешь метод, который возвращает JSON через raw string literal, добавь
/* lang=json*/
Этот момент нигде не задокументирован. Если в JSON будет синтаксическая ошибка вроде пропущенной запятой, Visual Studio подсветит её.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍49🔥16❤8👏2
This media is not supported in your browser
VIEW IN TELEGRAM
Тестируй API прямо в браузере
Есть опенсорсный инструмент Hoppscotch. Позволяет тестировать, дебажить и шарить API прямо из веб-браузера.
Отличная альтернатива Postman. Легкий и быстрый.
👉 @KodBlog
Есть опенсорсный инструмент Hoppscotch. Позволяет тестировать, дебажить и шарить API прямо из веб-браузера.
Отличная альтернатива Postman. Легкий и быстрый.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9
Конструкторное внедрение зависимостей и программирование через интерфейсы – основа хорошо тестируемого кода.
Слева: код Боба в PR
Справа: код Дейва в PR
Код Боба проходит ревью
Код Дейва заворачивают
Будь как Боб.
👉 @KodBlog
Слева: код Боба в PR
Справа: код Дейва в PR
Код Боба проходит ревью
Код Дейва заворачивают
Будь как Боб.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13💯2
Критические изменения в Entity Framework 10
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/breaking-changes
👉 @KodBlog
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/breaking-changes
Please open Telegram to view this post
VIEW IN TELEGRAM
Docs
Breaking changes in EF Core 10 (EF10) - EF Core
List of breaking changes introduced in Entity Framework Core 10 (EF10)
🤯1
Когда абстракции реально помогают
Хорошие абстракции изолируют то, что действительно меняется. То, что завтра может поехать.
Пример: платежи
Бизнес-логика не должна напрямую зависеть от SDK Stripe. Если захочешь перейти на Adyen или Braintree, не должно быть каскадного переписывания половины проекта. В таком случае абстракция полностью оправдана:
Теперь доменная логика не завязана на конкретного провайдера:
Абстракция закрывает нестабильную зависимость . Меняется только один класс, когда провайдеры выкатывают новые API или ты решаешь их сменить. Это хороший пример: он реально приносит пользу.
Когда абстракции превращаются в техдолг
Проблема начинается, когда абстрагируем то, что вообще-то стабильно. Получается лишний слой, который никому не нужен.
Репозиторий, который потерял смысл
Начиналось всё мило:
Но требования растут:
Интерфейс раздувается, запросы лезут прямо в контракты. А ведь EF Core уже умеет делать это из коробки через LINQ: типобезопасно, оптимизируемо и без излишних прокладок. Репозитории были полезны, когда ORM были кривые. Сейчас это часто просто лишнее.
Я тоже таким грешил. Но взросление разработчика — это понимать, когда паттерн становится анти-паттерном.
Сервисные обёртки:
Нормальный пример: обёртка над внешним API
Все детали HTTP и сериализации спрятаны внутри. Это даёт ценность.
Плохой пример: сервис-прокладка
Просто пробрасывание вызовов. Никаких правил, никакой логики. Таких слоёв становится много, а толку то ноль.
Как принимать нормальные решения
- Абстрагируй политику, а не механику
Политика меняется: платёжки, стратегии ретраев, кеши
Механика стабильна: LINQ, HttpClient, JSON
- Жди второй реализации
Одна реализация? Не спеши с интерфейсами.
Абстракция должна появляться из реальных требований, а не из фантазий.
- Разграничивай
Внутри приложения — конкретика
На границах системы — абстракции
Как выпиливать неудачные абстракции
Спроси себя: код станет проще без неё? Если да = смело удаляй.
👉 @KodBlog
Хорошие абстракции изолируют то, что действительно меняется. То, что завтра может поехать.
Пример: платежи
Бизнес-логика не должна напрямую зависеть от SDK Stripe. Если захочешь перейти на Adyen или Braintree, не должно быть каскадного переписывания половины проекта. В таком случае абстракция полностью оправдана:
public interface IPaymentProcessor
{
Task ProcessAsync(Order order, CancellationToken ct);
}
public class StripePaymentProcessor : IPaymentProcessor
{
public async Task ProcessAsync(Order order, CancellationToken ct)
{
// Stripe-специфика
}
}
public class AdyenPaymentProcessor : IPaymentProcessor
{
public async Task ProcessAsync(Order order, CancellationToken ct)
{
// Другая API, тот же бизнес-результат
}
}
Теперь доменная логика не завязана на конкретного провайдера:
public class CheckoutService(IPaymentProcessor processor)
{
public Task CheckoutAsync(Order order, CancellationToken ct) =>
processor.ProcessAsync(order, ct);
}
Абстракция закрывает нестабильную зависимость . Меняется только один класс, когда провайдеры выкатывают новые API или ты решаешь их сменить. Это хороший пример: он реально приносит пользу.
Когда абстракции превращаются в техдолг
Проблема начинается, когда абстрагируем то, что вообще-то стабильно. Получается лишний слой, который никому не нужен.
Репозиторий, который потерял смысл
Начиналось всё мило:
public interface IUserRepository
{
Task<IEnumerable<User>> GetAllAsync();
}
Но требования растут:
public interface IUserRepository
{
Task<IEnumerable<User>> GetAllAsync();
Task<User?> GetByEmailAsync(string email);
Task<IEnumerable<User>> GetActiveUsersAsync();
Task<IEnumerable<User>> GetUsersByRoleAsync(string role);
Task<IEnumerable<User>> SearchAsync(string keyword, int page, int pageSize);
Task<IEnumerable<User>> GetUsersWithRecentActivityAsync(DateTime since);
// и дальше понеслась
}
Интерфейс раздувается, запросы лезут прямо в контракты. А ведь EF Core уже умеет делать это из коробки через LINQ: типобезопасно, оптимизируемо и без излишних прокладок. Репозитории были полезны, когда ORM были кривые. Сейчас это часто просто лишнее.
Я тоже таким грешил. Но взросление разработчика — это понимать, когда паттерн становится анти-паттерном.
Сервисные обёртки:
Нормальный пример: обёртка над внешним API
public interface IGitHubClient
{
Task<UserDto?> GetUserAsync(string username);
Task<IReadOnlyList<RepoDto>> GetRepositoriesAsync(string username);
}
public class GitHubClient(HttpClient httpClient) : IGitHubClient
{
public Task<UserDto?> GetUserAsync(string username) =>
httpClient.GetFromJsonAsync<UserDto>($"/users/{username}");
public Task<IReadOnlyList<RepoDto>> GetRepositoriesAsync(string username) =>
httpClient.GetFromJsonAsync<IReadOnlyList<RepoDto>>($"/users/{username}/repos");
}
Все детали HTTP и сериализации спрятаны внутри. Это даёт ценность.
Плохой пример: сервис-прокладка
public class UserService(IUserRepository repo)
{
public Task<User?> GetByIdAsync(Guid id) => repo.GetByIdAsync(id);
public Task<IEnumerable<User>> GetAllAsync() => repo.GetAllAsync();
public Task SaveAsync(User user) => repo.SaveAsync(user);
}
Просто пробрасывание вызовов. Никаких правил, никакой логики. Таких слоёв становится много, а толку то ноль.
Как принимать нормальные решения
- Абстрагируй политику, а не механику
Политика меняется: платёжки, стратегии ретраев, кеши
Механика стабильна: LINQ, HttpClient, JSON
- Жди второй реализации
Одна реализация? Не спеши с интерфейсами.
// сначала просто класс
public class EmailNotifier
{
public Task SendAsync(...) { ... }
}
// потом появляется SMS, и вот тогда...
public interface INotifier
{
Task SendAsync(...);
}
Абстракция должна появляться из реальных требований, а не из фантазий.
- Разграничивай
Внутри приложения — конкретика
На границах системы — абстракции
Как выпиливать неудачные абстракции
Спроси себя: код станет проще без неё? Если да = смело удаляй.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍2
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
😁17🥴4👏3
В экосистеме
Репозиторий с исходниками:
https://github.com/karenpayneoregon/csharp-basics-2025/blob/master/AspCoreHelperLibrary/WindowHelper.cs
👉 @KodBlog
ASP.NET Core появилась небольшая, но полезная утилита для управления консольным окном. Разработчик выложил на GitHub пример кода, который позволяет принудительно показать консоль для логирования и менять её заголовок по своему вкусу. Инструмент уже протестирован на Windows 11 и может пригодиться тем, кто пишет сервисы с дополнительным выводом в терминал.Репозиторий с исходниками:
https://github.com/karenpayneoregon/csharp-basics-2025/blob/master/AspCoreHelperLibrary/WindowHelper.cs
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🤔1🥴1
В C# это довольно удобно: var завезли как раз тогда, когда добавили анонимные типы.
Хотя код тут чисто для демонстрации, забавно, что никто не заметил тупой баг на строке 4.
👉 @KodBlog
Хотя код тут чисто для демонстрации, забавно, что никто не заметил тупой баг на строке 4.
Please open Telegram to view this post
VIEW IN TELEGRAM
👎3❤1