EDITED by @stephentoub on 1/26/2023:
We don't want to reinvigorate the legacy pattern, so we can bury these helpers now as statics on the IAsyncResult interface itself... the only reason to reach for that interface is if you're using that legacy pattern, and these methods naturally fit there:
namespace System
{
public interface IAsyncResult
{
+ public static IAsyncResult BeginFromTask(Task task, AsyncCallback? callback, object? state);
+ public static void EndFromTask(IAsyncResult asyncResult);
+ public static TResult EndFromTask<TResult>(IAsyncResult asyncResult);
+ public static Task UnwrapTask(IAsyncResult asyncResult);
+ public static Task<TResult> UnwrapTask<TResult>(IAsyncResult asyncResult);
}
}
Alternatively, we can introduce a new class:
namespace System.Threading.Tasks
{
+ public static class TaskToAsyncResult
+ {
+ public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state);
+ public static void End(IAsyncResult asyncResult);
+ public static TResult End<TResult>(IAsyncResult asyncResult);
+ public static Task Unwrap(IAsyncResult asyncResult);
+ public static Task<TResult> Unwrap<TResult>(IAsyncResult asyncResult);
+ }
}
Stream provides both APM and Task methods for reading and writing. When creating a custom stream, you ideally override them both and provide a functional implementation. It is desirable to be able to implement this based on Task and possibly await just once and reuse that code for the APM methods.
The Framework does it that way in multiple places using the TaskToApm class (https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Threading/Tasks/TaskToApm.cs).
I suggest that the Framework should have a built-in way to do this. Possibly, the existing TaskToApm class can just be polished and made public.
EDITED by @stephentoub on 1/26/2023:
We don't want to reinvigorate the legacy pattern, so we can bury these helpers now as statics on the IAsyncResult interface itself... the only reason to reach for that interface is if you're using that legacy pattern, and these methods naturally fit there:
namespace System { public interface IAsyncResult { + public static IAsyncResult BeginFromTask(Task task, AsyncCallback? callback, object? state); + public static void EndFromTask(IAsyncResult asyncResult); + public static TResult EndFromTask<TResult>(IAsyncResult asyncResult); + public static Task UnwrapTask(IAsyncResult asyncResult); + public static Task<TResult> UnwrapTask<TResult>(IAsyncResult asyncResult); } }Alternatively, we can introduce a new class:
namespace System.Threading.Tasks { + public static class TaskToAsyncResult + { + public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state); + public static void End(IAsyncResult asyncResult); + public static TResult End<TResult>(IAsyncResult asyncResult); + public static Task Unwrap(IAsyncResult asyncResult); + public static Task<TResult> Unwrap<TResult>(IAsyncResult asyncResult); + } }Streamprovides both APM and Task methods for reading and writing. When creating a custom stream, you ideally override them both and provide a functional implementation. It is desirable to be able to implement this based onTaskand possiblyawaitjust once and reuse that code for the APM methods.The Framework does it that way in multiple places using the
TaskToApmclass (https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Threading/Tasks/TaskToApm.cs).I suggest that the Framework should have a built-in way to do this. Possibly, the existing
TaskToApmclass can just be polished and made public.