TGViewer
НеСерьезный шарпист НеСерьезный шарпист @serious_seesharp · 2.4K subscribers
Post #86 4.46K
Столкнулся с тем, что, как выяснилось, не всем очевидно для чего нужен 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 и улучшить производительность 👥.
  • ❤ 22
  • 👍 8
  • 🔥 2
More from @serious_seesharp
  1. Jul 11, 2026Знакомый создал бота @booster_tbot, для прохождения мок-собесов бесплатно, а вопросы основ…
  2. Jun 30, 2025Всем, привет! Мы сейчас начинаем отбор докладов на осеннюю конфу "Стачка" в Питере. Если у…
  3. Jun 5, 2025Post #85
  4. May 24, 2025Давно хотел сделать розыгрыш, и вот, кажется, время настало 🥰 Не надо никуда ничего скиды…
  5. May 23, 2025👩‍💻 Когда в C# 12 появились primary constructors (первичные конструкторы класс/структур)…
  6. May 21, 2025Вечером 29 мая состоится трансляция питерского .NET-митапа от SpbDotNet и Altenar Зрители…
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 →