Продвинутый C#-трюк: обновляй `Dictionary` без двойного поиска
Многие пишут так:
if (dict.TryGetValue(key, out var value))
{
dict[key] = value + 1;
}
else
{
dict[key] = 1;
}
Проблема: ты сначала ищешь ключ через TryGetValue, а потом снова лезешь в словарь через dict[key].
В hot path это лишняя работа.
Есть более взрослый вариант:
using System.Runtime.InteropServices;
ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(
dict,
key,
out var exists
);
if (!exists)
{
count = 0;
}
count++;
Что происходит:
1. C# получает ссылку прямо на значение внутри Dictionary
2. ключ ищется один раз
3. значение можно менять без повторного обращения
4. меньше лишних операций в tight loop
Где это полезно:
1. счётчики событий
2. парсеры
3. агрегации
4. обработка логов
5. high-performance backend code
Но есть важный нюанс:
не меняй структуру словаря, пока держишь ref.
То есть не делай Add, Remove, Clear рядом с этой ссылкой.
Это не трюк для каждого CRUD-сервиса.
Это инструмент для мест, где C# уже упёрся в производительность, и ты начинаешь выжимать лишние аллокации и лишние lookup’и.
Многие пишут так:
if (dict.TryGetValue(key, out var value))
{
dict[key] = value + 1;
}
else
{
dict[key] = 1;
}
Проблема: ты сначала ищешь ключ через TryGetValue, а потом снова лезешь в словарь через dict[key].
В hot path это лишняя работа.
Есть более взрослый вариант:
using System.Runtime.InteropServices;
ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(
dict,
key,
out var exists
);
if (!exists)
{
count = 0;
}
count++;
Что происходит:
1. C# получает ссылку прямо на значение внутри Dictionary
2. ключ ищется один раз
3. значение можно менять без повторного обращения
4. меньше лишних операций в tight loop
Где это полезно:
1. счётчики событий
2. парсеры
3. агрегации
4. обработка логов
5. high-performance backend code
Но есть важный нюанс:
не меняй структуру словаря, пока держишь ref.
То есть не делай Add, Remove, Clear рядом с этой ссылкой.
Это не трюк для каждого CRUD-сервиса.
Это инструмент для мест, где C# уже упёрся в производительность, и ты начинаешь выжимать лишние аллокации и лишние lookup’и.