Существует много ответов, которые показывают, как преобразовать события или асинхронные операции начала / конца в задачи. Этот код, тем не менее, не соответствует соглашениям ни одной из моделей. Это похоже на основанную на событиях асинхронную модель EAP без использования события. Если бы вы искали преобразования событий в задачи, вы бы нашли много ответов. Тем не менее, делегаты не используются для асинхронных операций, так как до EAP существовало соглашение о модели асинхронного программирования (APM) или Begin/End
.
.
Процесс процесса все тот же. Это описано в Взаимодействие с другими асинхронными шаблонами и типами .
Во всех случаях TaskCompletionSource используется для создания Задачи, сигнализирующей о завершении операции.
Когда класс следует соглашениям APM, можно использовать метод TaskFactory.FromAsync для преобразования пары Beging/End
в задачу. FromAsync
использует TaskCompletionSource под прикрытием для возврата Задачи, которая сигнализируется при вызове обратного вызова. Пример документа Interop для этого: Stream.BeginRead
:
public static Task<int> ReadAsync(this Stream stream,
byte[] buffer, int offset,
int count)
{
if (stream == null)
throw new ArgumentNullException("stream");
return Task<int>.Factory.FromAsync(stream.BeginRead,
stream.EndRead, buffer,
offset, count, null);
}
Использование делегатов аналогично использованию событий, что также показано в статье interop . Адаптированный к вопросу, он будет выглядеть примерно так:
public Task<bool> ConnectAsync(ThatService service)
{
if (service==null)
throw new ArgumentNullException(nameof(service));
var tcs=new TaskCompletionSource<bool>();
service.ConnectResultHandler=(ok,msg)=>
{
if(ok)
{
tcs.TrySetResult(true);
}
else
{
tcs.TrySetException(new Exception(msg));
}
};
return tcs.Task;
}
Это позволит вам использовать ConnectAsync
в методе async
, например:
public async Task MyMethod()
{
...
var ok=await ConnectAsync(_service);
...
}
Если msg
содержит данные об успехе, вы можете изменить ConnectAsync
на:
public Task<string> ConnectAsync(ThatService service)
{
if (service==null)
throw new ArgumentNullException(nameof(service));
var tcs=new TaskCompletionSource<string>();
service.ConnectResultHandler=(ok,msg)=>
{
if(ok)
{
tcs.TrySetResult(msg);
}
else
{
tcs.TrySetException(new Exception(msg));
}
};
return tcs.Task;
}
Вы можете изменить ConnectAsync
на метод расширения , который позволит вам использовать его, как если бы он был методом вашего класса обслуживания:
public static class MyServiceExtensions
{
public static Task<string> ConnectAsync(this ThatService service)
{
//Same as before
}
}
И используйте это:
public async Task MyMethod()
{
...
var msg=await _service.ConnectAsync();
...
}