| branch | build | coverage | quality |
|---|---|---|---|
| main | |||
| dev |
Given an infinite amount of time, everything that can happen will eventually happen... including needing to know at runtime if an object or type can be awaited.
The library provides the following extension methods:
using System.Threading.Tasks;
// Checks if it's awaitable
bool IsAwaitable(this object? instance);
bool IsAwaitable(this Type type);
// await x;
// Check if it's awaitable and returns a result
bool IsAwaitableWithResult(this object? instance);
bool IsAwaitableWithResult(this object? instance, out Type? resultType);
bool IsAwaitableWithResult(this Type type);
bool IsAwaitableWithResult(this Type type, out Type? resultType);
// var foo = await x;...and some bonus ones:
using IsAwaitable;
// Known awaitables: Task, Task<T>, ValueTask, ValueTask<T>
bool IsKnownAwaitable(this object? instance);
bool IsKnownAwaitable(this Type type);
// Is Task<T> or ValueTask<T>
bool IsKnownAwaitableWithResult(this object? instance);
bool IsKnownAwaitableWithResult(this object? instance, out Type? resultType);
bool IsKnownAwaitableWithResult(this Type type);
bool IsKnownAwaitableWithResult(this Type type, out Type? resultType);If you want to see how a type, or instance, is compliant with an awaitable expression, you can use the Awaitable type:
using IsAwaitable.Analysis;
_ = Awaitable.Describe("hello");
// null
var description = Awaitable.Describe(typeof(MyCustomAwaitableType));
if (description is not null)
{
var resultType = description.ResultType;
}
var taskDescription = Awaitable.Describe<Task>();
var isKnownAwaitable = taskDescripti.IsKnownAwaitable;The Describe function inspects the type to check if it matches the c# language specification for awaitable expressions:
An expression
tis awaitable if one of the following holds:
tis of compile time typedynamicthas an accessible instance or extension method calledGetAwaiterwith no parameters and no type parameters, and a return typeAfor which all of the following hold:
Aimplements the interfaceINotifyCompletionAhas an accessible, readable instance propertyIsCompletedof typeboolAhas an accessible instance methodGetResultwith no parameters and no type parameters
// On instances
Task doAsync = DoSomethingAsync();
_ = doAsync.IsAwaitable(); // true
// Returing a result
Task<int> promise = GetSomethingAsync();
_ = promise.IsAwaitable(); // true
_ = promise.IsAwaitableWithResult(); // true
// On types
_ = typeof(Task).IsAwaitable(); // true
// On value tasks
_ = typeof(ValueTask).IsAwaitable(); // true
_ = typeof(ValueTask<>).IsAwaitableWithResult(); // true
// On custom awaitables!
class CustomDelay
{
private readonly TimeSpan _delay;
public CustomDelay(TimeSpan delay) =>
_delay = delay;
public TaskAwaiter GetAwaiter() =>
Task.Delay(_delay).GetAwaiter();
}
var delay = new CustomDelay(TimeSpan.FromSeconds(2));
_ = delay.IsAwaitable(); // true
_ = delay.IsAwaitableWithResult(); // falseasync Task<object> AwaitResultOrReturn(object instance)
{
return instance.IsAwaitableWithResult()
? await (dynamic)instance
: instance;
}
var foo = GetFoo();
var fooTask = Task.FromResult(foo);
var result1 = await AwaitResultOrReturn(foo);
var result2 = await AwaitResultOrReturn(fooTask);
// foo == result1 == result2Created by The Icon Z from The Noun Project.
