Почему возвращаемый тип async должен быть void, Task или Task

Я пытаюсь испачкать руки с async CTP, и я заметил, что компилятор жалуется на тип возврата async. В чем проблема с другими типами?

простой демо

static void Main(string[] args)
{
    DoWork();
    Console.WriteLine("Returned to main");
    Console.Read();
}

// why do I need to return void, Task or Task<T> here?
// I know I can use something like Task<IEnumerable<string>>
private static async string[] DoWork()
{
    Console.WriteLine("DoWork started");
    return await Task.Factory.StartNew(
        delegate
        {
            Thread.Sleep(2000);                
            Console.WriteLine("DoWork done");
            return new List<string>();
        });        
}

2 ответов


на await [потребление] сторона, мы гибки: мы можем ждать любой тип покуда оно имеет правильные методы.

на async способ [производства], мы были непреклонны: мы жестко возвращает только тип задачи (или void). Почему разнобой?

  1. итераторы уже имеют это поведение...

    метод итератора (тот, который имеет "выход" внутри) жестко закодирован для возврата либо IEnumerable или Интерфейс IEnumerator. Однако вы можете "foreach" над любым типом, который имеет GetEnumerator/MoveNext / текущие участники. Таким образом, Async просто следует за suite.

  2. задача похожа на будущее, поэтому хорошо жестко ее закодировать...

    задача едва ли больше, чем будущее. Будущее-это основная фундаментальная часть языка / платформы. Нет причин для языка два имеют несколько копий такого фундаментального понятие. Одного достаточно. Это так фундаментально, что ты может даже добавить ключевые слова в язык для работы с будущим. В любом случае, если у кого-то есть будущее или более богатое представление о задаче, тогда они могут постройте его из задачи или Func. (Наши задачи уже выполняются. Если вы хотите построить что-то" холодное", например, F# asyncs или как IObservable, тот, который не запускается, пока вы не скажете Это – тогда вы должны постройте его из Func, а не из задачи).

  3. далее тонкости

    определите эту функцию:

    void f<T>(Func<Task<T>> f)
    

    и ссылаться на это:

    f( () => 1 + await t )
    

    мы хотели бы иметь возможность сделать вывод, что T=int в этом случае. Такой вывод невозможен, если только компилятор жестко зная, что лямбда она проходит на "F" типа Task<int>.

источник: техническое введение в асинхронный CTP


, потому что Task<TResult> - Это "будущее" - значение, которое появится позже. А string[] это то, что у вас есть прямо сейчас.

аналогично, Task - это операция, которая завершится (успешно или с ошибкой) когда-нибудь в будущем.

void является чем-то особенным случаем; он представляет собой операцию верхнего уровня в асинхронном CTP.

Если вам интересно, почему Task не выводится автоматически, это было рассмотрено, но отклонено команда асинхронного CTP. Их обоснование здесь и этой теме также покрывает ее.