CHT005: Direct ValueTask.Result access
Cause
The .Result property is accessed directly on a ValueTask<T>.
Rule Description
Accessing .Result directly on a ValueTask<T> is undefined behavior when the ValueTask is backed by an IValueTaskSource. This pattern:
- Is undefined behavior for
IValueTaskSource-backedValueTasks - May block if the operation hasn't completed
- Consumes the
ValueTask, preventing further usage - May throw or return incorrect results
The pooled primitives in CryptoHives.Foundation.Threading use IValueTaskSource for their ValueTask implementations, making this pattern particularly dangerous.
How to Fix
Option 1: Use await (Recommended)
Convert to async code:
// Before
ValueTask<int> vt = GetValueAsync();
int result = vt.Result; // Warning
// After
int result = await GetValueAsync();
Option 2: Convert to Task first
If you must access .Result synchronously:
// Before
ValueTask<int> vt = GetValueAsync();
int result = vt.Result; // Warning
// After
int result = GetValueAsync().AsTask().Result;
When to Suppress
Only suppress if you can guarantee:
- The
ValueTaskis already completed (IsCompletedSuccessfully == true) - You understand the
ValueTaskwill still be consumed
ValueTask<int> vt = GetValueAsync();
if (vt.IsCompletedSuccessfully)
{
#pragma warning disable CHT005
int result = vt.Result; // Safe because already completed
#pragma warning restore CHT005
}
Example
Violating Code
public int GetValue()
{
ValueTask<int> vt = GetValueAsync();
return vt.Result; // CHT005: undefined behavior
}
Fixed Code
// Option 1: Make async
public async Task<int> GetValueAsync()
{
return await GetValueInternalAsync();
}
// Option 2: Use AsTask()
public int GetValue()
{
return GetValueInternalAsync().AsTask().Result;
}
// Option 3: Check completion
public int GetValue()
{
ValueTask<int> vt = GetValueInternalAsync();
if (vt.IsCompletedSuccessfully)
{
return vt.Result;
}
return vt.AsTask().Result;
}