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
t
is awaitable if one of the following holds:
t
is of compile time typedynamic
t
has an accessible instance or extension method calledGetAwaiter
with no parameters and no type parameters, and a return typeA
for which all of the following hold:
A
implements the interfaceINotifyCompletion
A
has an accessible, readable instance propertyIsCompleted
of typebool
A
has an accessible instance methodGetResult
with 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(); // false
async 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 == result2
Created by The Icon Z from The Noun Project.