TGViewer
yet another dev yet another dev @yet_another_dev · 382 subscribers
Post #62 180
🤔 Почему value types в качестве ключа для IMemoryCache — плохая идея

С недавних пор в ASP.NET Core появилась встроенная поддержка кэширования. Пропустим шаги по его добавлению в ваше приложение, и перейдём сразу к сути.

Взаимодействовать с кэшем можно следующим образом:

// Добавление в кэш
_cache.Set(key, foo);
// Извлечение
_cache.Get<Foo>(key);

Какой тип данных лучше всего использовать в качестве ключа? Если вы думаете, что можно использовать значения типа int, long или структуры, то я вас разочарую.

📌 Во-первых, методы Set, Get, GetOrCreate, TryGetValue принимают ключ типа object. То есть даже если ваш ключ имеет тип значения, он всё равно будет упакован. Следовательно, сэкономить на аллокациях в куче не получится.

public static TItem Set<TItem>(
this IMemoryCache cache,
object key,
TItem value)

public static TItem? Get<TItem>(
this IMemoryCache cache,
object key)

public static TItem? GetOrCreate<TItem>(
this IMemoryCache cache,
object key,
Func<ICacheEntry, TItem> factory)

public static bool TryGetValue<TItem>(
this IMemoryCache cache,
object key,
out TItem? value)

📌 Во-вторых, если вы всё же решили использовать тип значения, будьте готовы к тому, что количество коллизий хэша возрастёт. Не спасут даже record struct. Рассмотрим пример:

var value = 1;
var key1 = new Key1(value);
var key2 = new Key2(value);
var key3 = new Key3(value);
var key4 = new Key4(value);

// value: 1
Console.WriteLine(
$"value: {value.GetHashCode()}");
// key1: 1
Console.WriteLine(
$"key1: {key1.GetHashCode()}");
// key2: 1
Console.WriteLine(
$"key2: {key2.GetHashCode()}");
// key3: 905109741
Console.WriteLine(
$"key3: {key3.GetHashCode()}");
// key4: -287359892
Console.WriteLine(
$"key4: {key4.GetHashCode()}");

public record struct Key1(int Id);
public record struct Key2(int Id);
public record class Key3(int Id);
public record class Key4(int Id);

Хэш код во всех случаях с типом значения равен 1. Такое поведение вызвано реализацией расчёта хэша для record struct – возвращается хэш поля int Id, то есть само значение Id.

[IsReadOnly]
[CompilerGenerated]
public override readonly int GetHashCode()
{
return EqualityComparer<int>.Default.GetHashCode(this.<Id>k__BackingField);
}

И поскольку под капотом у IMemoryCache находится самый обычный Dictionary, то при добавлении элементов с такими ключами произойдёт коллизия. Да, кэш всё ещё будет работать, но не так быстро, как это требуется.

Можно переопределить GetHashCode для структур, но зачем, если в конечном счёте значение всё равно будет упаковано.

С record class ситуация иная, поскольку хэш рассчитывается по-другому. Помимо значения Id учитывается ещё и тип.

[CompilerGenerated]
public override int GetHashCode() {
return EqualityComparer<Type>
.Default.GetHashCode(
this.EqualityContract) * -1521134295
+ EqualityComparer<int>
.Default.GetHashCode(
this.<Id>k__BackingField);
}

[CompilerGenerated]
protected virtual Type EqualityContract {
[CompilerGenerated]
get {
return typeof (Key4);
}
}


📌 Вывод

Я не знаю, почему в .NET не реализовали дженерик IMemoryCache, который бы позволил эффективно использовать структуры. Наверно на это были причины. Но до тех пор, не используйте значимые типы в качестве ключа для IMemoryCache, т.к. производительность такого решения хуже, чем со ссылочными типами.
  • 👍 4
More from @yet_another_dev
  1. Sep 21, 2026Опубликовал вчера ролик в одной запрещённой в России соцсети про то, как сходил на выборы.…
  2. Sep 20, 2026Мы пришли в 7:50 и очередь уже была 🥲 Пообщались с другими людьми. Многие приехали из дру…
  3. Sep 19, 2026Post #383
  4. Sep 18, 2026Последние пару недель на чат нападают боты со спамом (прикрыл стикером). Поэтому чат тепер…
  5. Sep 17, 2026Что интересного в этой статье: 1. Потрачено $120К, а агенты суммарно отработали около 3-х…
  6. Sep 17, 2026В Microsoft переписали рантайм GitHub Copilot с TypeScript на Rust при помощи агентов. Под…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →