Задача возврата вместо задачи <TResult> из TaskCompletionSource

Как я видел в нескольких примерах , а также, что я могу понять из этого Вопрос SO > Я должен иметь возможность вернуть не-общую задачу из TaskCompletionSource

(i.e., Return Task and not Task<TResult> from the method UploadFilesAsync) 

Однако следующий код:

public async Task UploadFilesAsync(string fileAPath, string fileBPath)
{
   var tcs = new TaskCompletionSource<Object>();

   //logic to process files

   try
   {
      await Task.WhenAll(uploadFileAAsync(fileAPath), 
                         uploadFileBAsync(fileBPath));
      tcs.TrySetResult(null);
   }
   catch (Exception e)
   {
      tcs.SetException(e);
   }
   finally
   {
      //logic to clean up files
   }
   return tcs.Task;
}

Производит следующую синтаксическую ошибку

'UploadFilesAsync(string, string)' is an async method that returns 'Task', 
a return keyword must not be followed by an object expression. 
Did you intend to return 'Task<T>'?

Я нацелен на .NET 4.5. Я знаю, что он может работать, чтобы вернуть задачу (объекта), но это делает интерфейс API "грязным". Предпочитает ли практика возвращать задачу (объекта) или можно вернуть задачу (не общее, как показано в коде)?

Ответ 1

Я знаю, что он может работать, чтобы вернуть задачу (объекта)

Ну, это не будет делать то, что вы ожидаете.

Проблема в том, что вы пытаетесь вернуть задачу... а метод async автоматически обертывает возвращаемое значение в другую задачу. Непонятно, почему вы используете метод асинхронного использования здесь, если быть честным. Почему бы просто не написать это:

public Task UploadFilesAsync(string fileAPath, string fileBPath)
{
    return Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
}

Разве это не делает то, что вы хотите? Вам просто нужна задача, которая завершается, когда завершаются обе "подоперации", верно? Именно это возвращает Task.WhenAll. Ваш метод по-прежнему не блокируется - он не будет ждать завершения операций до его возвращения. Просто вы используете тот факт, что Task.WhenAll не блокирует это, вместо метода async.

EDIT: обратите внимание: если вы хотите сделать что-то еще в этом методе, вы можете сделать его асинхронным методом, не используя TaskCompletionSource самостоятельно:

public async Task UploadFilesAsync(string fileAPath, string fileBPath)
{
    // Upload the files
    await Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
    await SomethingElseAsync();
    MaybeDoSomethingCheap();
}

Обратите внимание, что даже если метод async здесь возвращает Task, у вас нет возвращаемого значения - механизм async/await обрабатывает все это для вас, возвращая задачу, которая будет завершена, когда MaybeDoSomethingCheap() будет завершена, или ошибка, если выбрано исключение.

Ответ 2

Насколько я знаю, нет прямого способа вернуть объект Task при использовании TaskCompletionSource<T>.

Обычно я предпочитаю возвращать объект типа Task<bool> в этих ситуациях. Но вы правы в том, что возврат объекта типа общего назначения не имеет смысла, если возвращаемые значения функции не используются.

Но на самом деле вам не нужно создавать TaskCompletionSource, так как в этой функции есть ключевое слово await. TaskCompletionSource обычно используется для преобразования синхронной функции в асинхронную. Поскольку вы уже вызываете асинхронную функцию (и на самом деле это кажется единственной функцией), вам не нужно создавать TaskCompletionSource.

public async Task UploadFilesAsync(string fileAPath, string fileBPath)
{
        return await Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
}