Pull to refresh
2K+
-3
Павел Павлов@UnclePasha1979

User

-6,8
Rating
Send message

Статья #2: Сказ о том, как Dictionary дырявым стал, или Почему Пол «потерял» данные

Действующие лица:

  • Пол (МП): Год в индустрии, MacBook в наклейках, верит в магию фреймворков и то, что .NET сам всё порешает за его спиной.

  • Дядя Паша (ДП): 47 лет, архитектор старой закалки. Помнит времена, когда память выделяли дескрипторами, а за неэффективный алгоритм могли и из проекта попросить. Пьет Мальбек, смотрит на Пола как на жертву современного маркетинга.

Диалог

Пол: — Дядь Паш, ну это издевательство! .NET точно дырявый. У меня Dictionary<UserContext, string>, я туда записываю данные, а через секунду стучусь по точно такому же ключу — и KeyNotFoundException! Я дебажил три часа: поля в объекте идентичны, ID совпадает до бита. Где мои данные? Они что, протухли?

ДП: (медленно отрезает кусок эмпанады и смотрит на Пола с плохо скрываемой жалостью) — Эх, Пол... Жертва ты «быстрых курсов за 30 дней». Тебя там научили кнопки нажимать, а как шестеренки внутри хрустят — забыли. Ты мне скажи, соколик, как твой Dictionary поймет, что это один и тот же ключ, если ты ему каждый раз подсовываешь новый адрес в памяти?

Пол: — Ну как... Поля же одинаковые! UserId, TenantId. Разве он не внутрь объекта смотрит?

ДП: — Внутрь он посмотрит, когда ты его заставишь. А пока твой class UserContext — это ссылочный тип. Для рантайма твои два объекта — это как две одинаковые бутылки вина: этикетки одни, а пробки разные. Ты один объект в словарь положил, адрес его запомнил, а потом пришел с другим адресом. Для Dictionary это разные ключи. Он идет искать в другую «корзину» (bucket) и, естественно, находит там только дырку от бублика.

Пол: — И что теперь? Опять писать эту бесконечную портянку: Equals, GetHashCode, проверять на null, комбинировать поля? Это же прошлый век!

ДП: (прищуривается, делает глоток Мальбека) — Слушай сюда, инженер «счастливого будущего». В C# есть record. И это не просто «синтаксический сахар» для ленивых.

Запомни, Пол: Record — это архитектурный контракт на Value-based equality.

Когда ты пишешь public record UserContext(int Id), компилятор сам, за тебя, пишет правильный GetHashCode, который считается по значениям полей, а не по адресу. И Equals он пишет такой же. Для Dictionary два разных инстанса одного рекорда с одинаковыми данными будут одним и тем же ключом. Понимаешь? Одна строчка кода закрывает проблему, на которой вы жжете тысячи долларов облачного бюджета.

Пол: — Ладно, с «исчезновением» понятно. Но это же просто удобство, так? На производительность-то это как влияет?

ДП: (отставляет бокал и смотрит на Пола, как на человека, утверждающего, что старый фермерский пикап и болид Формулы-1 едут одинаково, потому что у обоих по четыре колеса)

— «Просто удобство»? Эх, Пол... Ты хоть раз заглядывал под капот? В реальном проекте, когда ты меняешь свой тяжелый класс на компактный record, скорость поиска в Dictionary взлетает в три раза. Минимум!

Пол: — В три раза?! Да ну, за счет чего? Это же тот же самый поиск в хэш-таблице!

ДП: — За счет инженерной точности. Компилятор генерирует для рекорда максимально эффективный код: без лишних кастов и с идеально подогнанным вычислением хэша. Твой рекорд залетает в пазы Dictionary как деталь от швейцарских часов, а твой класс — как ржавый болт, который нужно еще полчаса подпиливать напильником.

Пол: — Слушай, дядь Паш... Я ведь реально думал, что Dictionary — это просто. А тут — магия адресов, хэш-коллизии, контракты рекордов... Откуда ты всё это знаешь?

ДП: — Я это не помню, я это знаю. Потому что в наше время учили строить фундамент, а не клеить обои на гнилые стены. Современные курсы тебе этого не скажут — им выгодно, чтобы ты бесконечно покупал их «продвинутые уровни».

— Хочешь знать, где еще у тебя «дыры»? Я систему собрал. Там 15+ тысяч вопросов, которые вытрясут из тебя всю дурь и покажут, где ты реально профи, а где просто заголовки на Medium читал.

— Называется «Я хочу знать .NET». Ссылка у тебя есть — iwanttoknow.net. Пройдешь раздел по коллекциям без единого мата — налью тебе Мальбека. А пока — иди, переделывай.

Tags:
-12
Comments0

Information

Rating
Does not participate
Registered
Activity

Specialization

Фулстек разработчик, Архитектор программного обеспечения
Ведущий
From 4,000 $
Git
PostgreSQL
SQL
Linux
ООП
C#
.NET
Entity framework
ASP.NET
.NET Core