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 и улучшить производительность 👥.