НеСерьезный шарпист
СтатистикаРефлексируем о .NET под пивом с ц# По всем вопросам: @l0c4lhost
- Последний пост
- 11 июл.
- Последнее чтение
- 13:58
- Постов за неделю
- 0
- Всего постов
- 22
- Тип
- открытый
- Язык
- русский
- Категория
- Технологии (по похожим)
- В каталоге с
- 15 авг.
- 1/24сутки в ленте
- —
- 1/48двое суток
- —
- 1/72трое суток
- —
Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.
Посты
Знакомый создал бота @booster_tbot, для прохождения мок-собесов бесплатно, а вопросы основаны на основе анализа практически трёхсот собеседований. Так что это не классические списки одних и тех же топ 100 вопросов по технологии X, которые все щитпостят из года в год на всяких хабрах, а действительно нормальный инструмент для подготовки. Пользуйтесь 🔥 А еще он у себя на канале @dotnetdad сейчас разыгрывает книжку по микросервисам, подарит первому, кто на синька пройдет 😱
Всем, привет! Мы сейчас начинаем отбор докладов на осеннюю конфу "Стачка" в Питере. Если у вас есть идея интересного доклада - подавайтесь или пишите мне в лс. Это может быть: 🟢живой кейс из продакшена, с которым вы реально справились (или нет), 🟢архитектурные решения, которые сработали в бою, 🟢опыт миграции, оптимизации, тонкой настройки .NET/ASP .NET/Core/EF/etc, 🟢или просто тема, которая вас драйвит, и вы хотите рассказать о ней глубже. Буду читать все заявки на .NET секцию, помогу докрутить идею и выстроить подачу, если это нужно. Если вы никогда не выступали — не страшно, наоборот, хочется видеть новых спикеров. Поддержка будет на всех этапах. 📌https://nastachku.ru/ 📆 Конфа пройдёт 2-3 октября в Питере 📞 Дедлайн подачи заявки пока установлен на 15.07, но можем и продлить Если сомневаетесь, пишите мне в личку (есть в описании канала) или в сообщения канала. Обсудим, направим, поможем.
Столкнулся с тем, что, как выяснилось, не всем очевидно для чего нужен ValueTask и когда его стоит использовать. Что ж 🤨 ValueTask<T> - это структура-обертка, которую стоит использовать вместо Task<T> в тех случаях, когда результат может быть получен синхронно, что позволит избежать лишней аллокации объекта Task в куче. Однако ValueTask не должен использоваться в качестве возвращаемого значения во всех под ряд методах, т.к. это может привести к проблемам с производительностью. НЕ используйте ValueTask<T>, если выполняются хотябы одно условие: ❌ Метод всегда выполняется асинхронно. ❌ Метод объявлен как async (всегда будет все равно создаваться AsyncStateMachine, поэтому ValueTask будет бессмысленен). ❌ Метод редко вызывается — выигрыш от избежания аллокации Task незначителен. ❌ Метод явялется публичным API библиотеки. ❌ Используется .Result, .GetAwaiter().GetResult(), .AsTask() или повторный await. public async ValueTask<User?> GetUserAsync(Guid id) { var result = await dbContext.Users.FirstOrDefaultAsync(u => u.Id == id); return result; } Используйте ValueTask<T>, если выполняются все условия: ✅ Результат может быть получен синхронно (например, из кэша). ✅ Метод не использует async/await. ✅ Метод вызывается часто и критичен по производительности, где аллокации Task значимы. ✅ Нет необходимости повторно ожидать ValueTask. ✅ Вызов Task-версии метода означает ненужную аллокацию, которую ValueTask может избежать. public ValueTask<User?> GetUserAsync(Guid id) { if (_cache.TryGetValue(id, out var user)) { return ValueTask.FromResult<User?>(user); } return new ValueTask<User?>(LoadFromDatabaseAsync(id)); } private async Task<User?> LoadFromDatabaseAsync(Guid id) { var result = await dbContext.Users.FirstOrDefaultAsync(u => u.Id == id); return result; } Когда метод возвращает ValueTask<T>, возможны 2 сценария: 1) Если результат уже есть, метод возвращает ValueTask.FromResult(). Результат хранится внутри структуры, лишних аллокаций нет, await завершится сразу. 2) Если нужно дождаться асинхронной операции, ValueTask оборачивает обычный Task<T>, и await работает как обычно. Аллокация есть — но только в этом случае. Таким образом, не стоит навешивать везде ValueTask. Если метод вызывается десятки тысяч раз в секунду, тогда действительно ValueTask может существенно снизить нагрузку на GC и улучшить производительность 👥.
видео или голосовое, без подписи
Давно хотел сделать розыгрыш, и вот, кажется, время настало 🥰 Не надо никуда ничего скидывать, на кого-то ещё подписываться, просто кликайте одну кнопку и принимайте участие в розыгрыше пяти книг, которые включены в мой личный топ полезнейших книг. 1. Библия .NET разработчика — "CLR via C#" Джеффри Рихтер 2. Шикарнейшая литература по построению приложений — "Высоконагруженные приложения" Мартин Клеппман 3. Три экземпляра "Микросервисы" Крис Ричардсон. Куда сейчас без микросервисов? Дата подведения итогов — 13.06.2025 12:00
👩💻 Когда в C# 12 появились primary constructors (первичные конструкторы класс/структур), то я почему-то думал, что параметры данного сахара разворачиваются в приватные ридонли поля, для обеспечения иммутабельности. Однако по какой-то причине авторы ушли в другую сторону, и это теперь даже не поля)) Да, в ранних версиях предложения первичного конструктора, он должен был работать так же, как в record типах, но генерирующим приватные поля вместо открытого свойства. Однако во время разработки команда C# решила изменить поведение. И имеем следующее, что параметры первичного конструктора — это просто переменные, как в обычном конструкторе, но они оказываются в области видимости всего типа. То есть по факту данные параметры не влияют на состояние объекта и мы не можем, например, обратиться к ним через this, это просто не скомпилируется. И вроде бы кажется, что область видимости приватная и все должно быть нормально, т.к. ни один здравомыслящий разработчик не будет брать и переопределять эти первичные параметры... Но, зная, какие кадры бывают на проектах и как они это все могут разломать, то я бы не надеялся. И тут есть несколько вариантов перестраховаться. Самый простой - запрет на использование primary constructor. И за это можно получить кучу навоза в лицо от коллег, которые будут отстаивать, что все безопасно и все пишут хороший код. Второй вариант - явное создание readonly полей с такими же наименованиями, что и у параметров первичного конструктора. В таком случае параметры перекроются и не будут больше доступны после присвоения полям. 😡 Плохо public class GetRequestQuery( ReadOnlyContext context, IEquipmentService equipmentService) { // ... } 👍 Хорошо public class GetRequestQuery( ReadOnlyContext _context, IEquipmentService _equipmentService) { private readonly ReadOnlyContext _context = _context; private readonly IEquipmentService _equipmentService = _equipmentService; // ... } 👍 И есть еще один интересный инструмент на сорс генераторах, который мне посоветовал товарищ СтепВан. Он позволяет нам написать все необходимые readonly поля, и с ними будет сгенерирован коснтруктор. Выглядит весьма прикольно, но с колегами говном покидаться все равно придется. [PrimaryConstructor] public partial class GetRequestQuery { private readonly ReadOnlyContext _context; private readonly IEquipmentService _equipmentService; // ... }
Вечером 29 мая состоится трансляция питерского .NET-митапа от SpbDotNet и Altenar Зрители онлайна смогут задавать вопросы спикерам. Регистрируйтесь, чтобы не потерять ссылки на трансляции и получить доступ к записям. • В 19:00 (мск) Владимир Куропатка расскажет, как добиться высокой производительности и отказоустойчивости при использовании Serilog в проектах. • В 20:30 Константин Финагин покажет, как писать простые и наглядные юнит-тесты — без тонущих в ассертах простыней кода. Подробности о докладах и митапе — на странице регистрации. Увидимся в эфире!
Утечки памяти в .NET 🐒 Слышал различные мнения на этот счёт и то, что это всё не утечки и их в дотнете не существует, но давайте посмотрим на определение из вики: Уте́чка па́мяти (англ. memory leak) — процесс неконтролируемого уменьшения объёма свободной оперативной или виртуальной памяти компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих память от ненужных данных Иначе говоря простыми словами, утечка памяти — это сценарий, когда память не освободилась, а должна была. Давайте прикинем как такое возможно в дотнете на примере трёх не самых очевидных вариантов🥰 1. Забытые подписки на события Пока у события есть подписчики оно не будет очищено сборщиком мусора. public class EventPublisher { public event EventHandler SomeEvent; public void RaiseEvent() { SomeEvent?.Invoke(); } } public class Subscriber { public Subscriber(EventPublisher publisher) { // Подписываемся на событие publisher.SomeEvent += OnSomeEvent; // Но никогда не отписываемся! } private void OnSomeEvent(object sender, EventArgs e) { Console.WriteLine("Event received"); } } 2. Статические поля и коллекции Статические поля живут на протяжении всего времени работы приложения. Если они содержат ссылки на большие объекты или продолжают расти, это может привести к утечкам: public static class Cache { // Эта коллекция будет только расти private static readonly Dictionary<string, object> _items = new Dictionary<string, object>(); public static void Add(string key, object value) { _items[key] = value; } public static object Get(string key) { return _items.TryGetValue(key, out var value) ? value : null; } } 3. Замыкания и захват переменных Анонимные методы и лямбда-выражения могут захватывать переменные из внешней области видимости, что иногда приводит к неожиданным утечкам: public class LambdaExample { public Action CreateLongLivingAction() { // Этот массив будет жить до тех пор, пока жив возвращаемый делегат var largeArray = new byte[1000000]; return () => { Console.WriteLine(largeArray.Length); }; } } Лайк, шер, ретвит и, может быть, когда-нибудь техника полностью меня одолеет 🌟
В этом году я состою в Программном Комитете конференции Стачка и отвечаю за секцию C# Приглашаю СтепВанчиков выступить с годным контентом Если у вас только идея, пишите - доработаем и дойдём до доклада Конференция пройдёт в Ульяновске 18-19 апреля, участие оффлайн Вся информация тут👇 Информация спикерам: https://ul25.nastachku.ru/to-do-speaker-ul25 Регистрация: https://ul25.nastachku.ru/users-new Подача доклада: https://ul25.nastachku.ru/lectures-new
Привет, бедолаги! Если вы идете путем СИЛЬНЫХ 😎 и не хотите крутить опыт, я сейчас к себе на проект ищу стажера. К сожалению, во время стажировки удаленки нет, а данная позиция открыта в Уфе, поэтому актуально только для уфимцев (ну, либо вы хотите 3.5 месяца потусоваться в Уфе и походить там в офис:). Если вы не из Уфы, можете податься на стажерские позиции в других городах, в компании сейчас идет набор стажеров на весенний поток стажки. И, да, для отбора нужно сделать тестовое задание. Пока я получил 5 слабеньких решений и все. Потом после отбора на приватном канале сделаю видео с разбором основных ошибок, покажу, как следовало подойти к решению. Вакансия 👉 https://spb.hh.ru/vacancy/115482009 Место, где оставлять заявку 👉 https://career.infotecs.ru/intern/
Антипаттерн Lazy Loading Lazy Loading является механизмом EF Core, с помощью которого неявно загружаются связанные сущности при первом обращении к их навигационным свойствам. Для того, чтобы EF Core мог использовать такой тип загрузки связанных данных, все навигационные свойства модели должны быть virtual. public class Library { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<Book> Books { get; set; } } А также при конфигурировании контекста необходимо вызвать UseLazyLoadingProxies(), включив поддержку прокси. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseLazyLoadingProxies() // Включение прокси для Lazy Loading .UseNpgsql("Host=localhost;Database=mydb;"); } Теперь необязательно при вытягивании Library (context.Library.FirstOrDefault()) необязательно явно делать получение Books через Include или Load, будет достаточно просто обратиться к данному свойству и получим необходимые данные. Но как это работает, минусы будут, почему это антипаттерн, когда так все упрощается?! Lazy Loading реализуется через динамические прокси-объекты, т.е. оно работает на основе наследования и переопределения виртуальных свойств. Когда мы запрашиваем сущность (context.Library.FirstOrDefault()), EF не возвращает саму сущность, а создаёт наследника от неё в рантайме! Таким образом EF создаст динамический подкласс LibraryProxy, который наследуется от Library и переопределяет свойство Books таким образом, что при первом запросе автоматически выполняется sql-запрос к бд для загрузки данных в коллекцию (SELECT * FROM Books WHERE LibraryId = @Id), а при последующих данные будут доставаться уже из коллекции из памяти. И к чему это может привести?! Как минимум, это уже неэффективно из-за того, что для генерации в рантайме таких прокси-классов EF использует Castle.DynamicProxy, использующий в свою очередь рефлексию, а использование виртуальных таблиц - это дополнительные расходы по памяти и по времени запроса. А как максимум, мы получаем кучу неявных обращений к БД (которые в ряде случаев нужно упаковать в один запрос), и потенциальную проблему N + 1 🤡. ⚠️ Проблема "N+1 запросов": Если мы загружаем 10 Library и потом в цикле обращаемся к Books, это приведёт к 10 дополнительным запросам к БД. (А если таких сущностей 1000?)) Таким образом, данный подход ведет только к потенциальным проблемам с производительностью системы, а плюсы в удобстве, что не нужно явно прописывать загружаемые сущности в запросе, весьма сомнительны. Не знаю ни одного кейса, где Lazy был бы действительно полезен. 🤡 Допустим, вам достался проект от предыдущей некомпетентной команды, которая взяла и втащила Lazy на глобальном уровне, и в куче мест получаем проблемы с производительностью. Что делать, как избавляться от данного говна?) Как вариант, стоит договориться с командой в новом коде загружать данные только явным образом и для начала убрать Lazy на глобальном уровне и прописать его для всех моделей сущностей отдельно через Delegate-based Lazy Loading, что решит проблему с излишней кодогенерацией и позволит постепенно выпиливать Lazy для отдельных сущностей. public class Library { private readonly ILazyLoader _lazyLoader; private ICollection<Book> _books; public Library() { } public Blog(ILazyLoader lazyLoader) { _lazyLoader = lazyLoader; } public int Id { get; set; } public string Name { get; set; } public ICollection<Book> Books { get => _lazyLoader.Load(this, ref _books); set => _books = value; } } Однако такой подход не решает проблему N+1 и нужно все равно искать данные места и исправлять их, переводя загрузку сущностей на явную, а не через 100500 отдельных неявных запросов.
Большой Шарпизм Начинаем забирать 2025) Сидел на днях, и в голову пришла мысль - есть svo ремиксы, гачи ремиксы, даже рыбалка ремиксы... А айти ремиксов никто не завёз И тогда я решил открыть этот жанр - бац, готов текст и сегодняшняя запись на студию Теперь можете послушать, что называется, с пылу жару Присылайте своим любимым айти блогерам, чтобы они тоже подключались к движухе и делали вещи Джависты будут повержены 💪
В .NET 8 были выпущены интересные изменения в библиотеке Microsoft.Extensions.Hosting, на которые многие почему-то не обратили внимания. Данная либа предоставляет возможности для создания фоновых задач в шаблоне ASP.NET приложения, а также для создания отдельных…
В .NET 8 были выпущены интересные изменения в библиотеке Microsoft.Extensions.Hosting, на которые многие почему-то не обратили внимания. Данная либа предоставляет возможности для создания фоновых задач в шаблоне ASP.NET приложения, а также для создания отдельных Worker-сервисов без привязки к веб-серверу (что, например, хорошо подходит для создания микросервисов, работающих с очередью брокера сообщений). Приложение может содержать одну или несколько таких задач, реализующих IHostedService и регистрирующихся в контейнере внедрения зависимостей. Microsoft предоставляет готовую реализацию интерфейса IHostedService в виде базового абстрактного класса BackgroundService, предоставляет общую менимальную логику методов StartAsync() и StopAsync(), которая достаточно большинству фоновых задач. Чтобы использовать BackgroundService, необходимо унаследоваться от данного класса и реализуют абстрактный метод ExecuteAsync() с логикой фоновой задачи. public class WorkerServiceOne : BackgroundService { private readonly ILogger<WorkerServiceOne> _logger; public WorkerServiceOne(ILogger<WorkerServiceOne> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Worker 1 running at: {time}", DateTimeOffset.Now); await Task.Delay(1000, stoppingToken); } } } Для запуска служб создаем объект хоста и запускаем его. Вместе с запуском хоста будут запущены и все зарегистрированные службы. IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<WorkerServiceOne>(); services.AddHostedService<WorkerServiceTwo>(); }) .Build(); host.Run(); Таким образом, каждая задача IHostedService, зарегистрированная в di-контейнере, запускается последовательно путем вызова метода StartAsync(). То есть для запуска следующий задачи выполняется ожидание завершения метод StartAsync() предыдущей задачи. Очевидно, что медленный запуск одной из служб тормозит развертывание всего приложения. А при остановке приложения выполняется такая же синхронная логика с StopAsync(). Однако для StopAsync() настраивается таймаут завершения и нужно учитывать время завершения каждого конкретного сервиса, а если завершение одной из служб будет занимать слишком много времени, весь процесс завершения приложения может завершаться аварийно. Для решения этих проблем в .NET 8 была добавлена возможность "параллельного" запуска и завершения, регистрируемых служб. Для этого теперь существуют опции конфигурации ServicesStartConcurrently и ServicesStopConcurrently. IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.Configure<HostOptions>(options => { options.ServicesStartConcurrently = true; options.ServicesStopConcurrently = true; }); services.AddHostedService<WorkerServiceOne>(); services.AddHostedService<WorkerServiceTwo>(); }) .Build(); host.Run(); Сейчас код реализации "параллельного" запуска служб хоста выглядит примерно следующим образом: if (_options.ServicesStartConcurrently) { List<Task> tasks = new List<Task>(); foreach (IHostedService hostedService in _hostedServices) { tasks.Add(Task.Run(() => StartAndTryToExecuteAsync(hostedService, combinedCancellationToken), combinedCancellationToken)); } Task groupedTasks = Task.WhenAll(tasks); } То есть задачи по-прежнему запускаются в том же порядке, в котором они регистрируются в di, но они не ожидаются на этом этапе. Задачи добавляются в список, и только после запуска каждой задачи WhenAll используется для ожидания завершения StartAsync для каждой службы. В этом режиме запуск займет столько же времени, сколько самый медленный вызов StartAsync, что позволяет приложению в целом запуститься быстрее. Аналогичная история и с StopAsync.
Ну что же, мы готовы анонсировать менторство до оффера без предоплаты 🥰 Вы платите только по факту трудоустройства 100% от суммы оффера, которую можно разбить на несколько месяцев. Никаких штрафов, скрытых условий и мелкого шрифта, всё только по факту трудоустройства. Шах и мат скиллКоробке и гугл теориуму с менторами, которые сами не могут найти работу. У нас всего два ментора: я и @sanazarov_live — руководитель разработки бэкенда в биг-тех компании. Где-то ещё есть такие менторы? 🌟 Пишите в ЛС — @mccalen, берём не всех подряд, но я с каждым обязательно созвонюсь лично и отвечу на все интересующие вопросы. @dotnetdad
Вызов асинхронных методов в синхронном коде В C# код, использующий асинхронность, должен следовать принципу: "async всё", то есть если есть асинхронный метод, его нужно вызывать в асинхронном контексте. Однако бывает возникают ситуации, когда нужно вызвать асинхронный метод из синхронного кода (например, в старом коде, в desktop UI-приложениях). Существуют несколько подходов для таких вызовов, рассмотрим основные из них: 1. Task.Result и Task.Wait() Самый простой способ — использовать свойство Result или метод Wait(), чтобы блокировать выполнение до завершения асинхронной задачи. public int GetCountMethod() { var task = GetCountMethodAsync(); task.Wait() return task.Result; } или public int GetCountMethod() { var task = GetCountMethodAsync(); return task.Result; } Task.Wait() метод блокирует поток до тех пор, пока не taskбудет успешно выполнен. Task.Result также блокирует вызывающий поток, пока результат не готов, как только задача будет завершена, возвращается результат. Основной проблемой данного способа является возможный deadlock, который может произойти, если вызывающий поток ожидает завершения асинхронной задачи, которая пытается вернуться на основной поток для выполнения продолжения из-за контекста синхронизации, что может произойти в UI-приложениях. Еще одним минусом является то, что в случае возникновения исключения оно будет обернуто в AggregateException, который придется раскручивать для получения действительного исключения. 2. GetAwaiter().GetResult() Данный способ аналогичен предыдущему, но будет более предпочтительнее, т.к. не оборачивает возникающие исключения в AggregateException. public int GetCountMethod() { var result = GetCountMethodAsync().GetAwaiter().GetResult(); return result; } Однако такой подход также может привести к deadlock'ам 3. Task.Run() Можно использовать Task.Run() для выполнения асинхронного кода в отдельном фоновом потоке, что решает проблему deadlock. public int GetCountMethod() { var result = Task.Run(async () => await GetCountMethodAsync()).GetAwaiter().GetResult(); return result; } 4. async Main А для того, чтобы не использовать весь этот цирк, если планируем использовать асинхронный код, то делаем асинхронную точку входа.
SynchronizationContext Если упрощать и не вдаваться в точные определения SynchronizationContext в .NET позволяет передавать между потоками задачи (делегаты) на выполнение. В десктоп приложениях WinForms/WPF/UWP, например, все задачи, связанные с обновлением интерфейса, должны выполняться в основном Main потоке. Когда приложение запускается, оно автоматически создает контекст синхронизации для этого потока. Если выполнение кода уходит в фоновый поток, SynchronizationContext позволяет вернуться обратно к основному UI-потоку. При обращении к UI из другого фонового потока получим исключение. private void SomeWork() { SynchronizationContext context = SynchronizationContext.Current; Task.Run(() => { Thread.Sleep(1000); // Передача задачи на UI поток context.Post(() => { StatusLabel.Content = "Done!"; }, null); }); } При асинхронном выполнении, когда мы ставим задачу на ожидание await, среда выполнения автоматически сохраняет текущий SynchronizationContext и после завершения асинхронной операции возвращает выполнение в этот контекст. Таким образом, не нужно вручную управлять потоками, контекст синхронизации делает это за нас. private async void Button_Click(object sender, RoutedEventArgs e) { await Task.Run(() => { Thread.Sleep(1000); }); // После await будет продолжено выполнение в UI потоке StatusLabel.Content = "Done!"; } Зачастую бывает не обязательно возвращаться в исходный поток после асинхронной операции. В таких случаях можно отключить использование контекста с помощью метода ConfigureAwait(false) и позволить продолжить выполнение кода, после завершения асинхронной операции, в любом доступном потоке. await WorkAsync().ConfigureAwait(false); А чтобы каждый раз не прописывать ConfigureAwait(false), можно переключиться на пул потоков с помощью AsyncHelper.RedirectToThreadPool(). А теперь поговорим про ASP.NET))) В ASP.NET Core НЕТ SynchronizationContext. Проблема такого подхода для веб приложения довольно очевидна. HTTP-запрос в старой версии ASP.NET обрабатывался в рамках отдельного потока из пула потоков. После того как поток назначался для обработки запроса, он оставался связан с запросом до тех пор, пока обработка не завершалась, за что отвечал контекст синхронизации SynchronizationContext, гарантировавший продолжение выполнения кода в рамках запроса на одном и том же потоке. По окончанию асинхронной операции, продолжение кода асинхронного метода ставилось в очередь на продолжение выполнения в данном потоке, после чего происходит ожидание выполнения всех других "продолжений". Когда оно готово к запуску, поток берется из пула потоков, входит в контекст запроса, а затем возобновляет выполнение обработчика. Таким образом, имеем проблему с производительностью и масштабируемостью из-за потребности в большом количестве потоков в пуле. При использовании подхода ASP.NET Core без контекста каждый запрос может быть обработан любым доступным потоком, а при асинхронной операции, когда асинхронный обработчик возобновляет выполнение, берется любой свободный поток из пула и выполняет продолжение. Очередь контекста избегается, и нет необходимости «входить» в контекст запроса. Соответственно, сейчас отпадает потребность писать ConfigureAwait(false), т.к. это нам по факту ничего не даст)) Еще забавляет, когда интервьюеры на собесах на позицию ASP.NET разработчика спрашивают про ConfigureAwait(false), ожидая ответа про отвязку от контекста и выполнение в любом потоке пулла, когда их проект на .NET 8))) В сентябре был на 10+ технических собесах и данный вопрос был в процентах 40 случаев. Хотя чего я хочу, когда CTO одной галеры пытался доказывать мне, что Dictionary - НЕ хештейбл 🤡 P.S. Да, мое месячное отсутствие связано с прохождением кучи собесов. Получил 5 офферов, выбрал классный проект, но не смогу перейти сейчас из-за возможных проблем с военкоматом при смене компании 💧
видео или голосовое, без подписи
Васап, товарищи шарписты?! Как я и говорил, сегодня начали розыгрыш 7 книг Марка Симана "Внедрение зависимостей в .NET" в честь старта нашего общего канала @csharpcommon. Целью канала является создание площадки для комьюнити C# разработчиков без какой-либо цензуры для популяризации шарпа, общения, созвонов с докладами, возможно в последствии даже проведения собственных митапов и конф 😎, также будем периодически разыгрывать качественную на наш взгляд литературу🥂. По условиям текущего розыгрыша все предельно просто, нужно быть подписанным на все 4 канала: мой, общий и каналы коллег (@steponeit, @dotnetdad)
Пара организационных моментов. По поводу рекламы на канале, реклама иногда будет появляться, т.к. буду продавать рекламные места за много деняг 🥂. Это позволит разыграть крутые книжки, пустить средства на развитие сообщества. Но реклама - это не рекомендация, за рекомендации мне никто не платит и, если я что-то советую (прямым текстом написано "рекомендую"), то это бесценно) По поводу вкусного (розыгрыш). Мы тут создали своместный канал @csharpcommon, где будем рассматривать различные новости индустрии, делать созвоны с докладами на тему шарпа/разработки, где будем также очень экспертно отвечать на все вопросы. По текущему состоянию понятно, что будет много рофло-контента 🤡, но если вас с такого не корежит, подписывайтесь. Наша цель сделать крутое сообщество .NET разработчиков. А, и, да, скоро будет розыгрыш книг Марка Симана (уже заказали 😎) Градус кринжа по унижению джавистов надо поднять @csharpcommon 💪