Почему этот асинхронный/ожидающий код генерирует не все пути кода, возвращающие значение?

Надеюсь, это не повторение, но здесь есть более 5000 вопросов с «не все пути кода возвращают значение»!

Проще говоря, почему этот метод с неуниверсальной реализацией прекрасно компилируется:

    public static async Task TimeoutAfter(this Task task, int millisecondsTimeout)
    {
        if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout)))
            await task;
        else
            throw new TimeoutException();
    }

в то время как эта попытка сделать метод универсальным генерирует предупреждение/ошибку Return state missing/...not all code paths return a value?:

    public static async Task<T> TimeoutAfter<T>(this Task<T> task, int millisecondsTimeout)
    {
        if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout)))
            await task;
        else
            throw new TimeoutException();
    }

person HolySamosa    schedule 24.08.2012    source источник
comment
@L.B *должен вернуть Task и ...   -  person Chris Laplante    schedule 24.08.2012


Ответы (2)


Необщий тип Task в чем-то эквивалентен ожидаемому методу void. Как и в случае метода void, вы не можете ничего вернуть из метода с типом возвращаемого значения Task, поэтому первый пример компилируется. Однако во втором примере ожидается возвращаемое значение универсального типа, и вы не предоставляете его в пути, где вы ожидаете другого вызова.

Цитата из справочника MSDN по ключевому слову async, особенно по типам возвращаемых значений.

Вы используете Task, если после завершения метода не возвращается осмысленное значение. То есть вызов метода возвращает Task, но когда Task завершается, любое выражение await, ожидающее Task, оценивается как void.

person Chris Hannon    schedule 24.08.2012
comment
Насколько я понимаю, значение выражения ожидания применяется к возвращаемому значению, поэтому оно не эквивалентно возврату Void, однако ожидание само по себе равно System.Void. - person David Anderson; 24.08.2012
comment
Это правда, что метод неявно возвращает Task, который вы можете затем ожидать. Это эквивалентно void в том смысле, что вам не разрешено возвращать значение из метода самостоятельно, и ничего не может быть назначено в результате ожидания этого метода. В случае Task<T> возвращаемое вами T будет назначаться из ожидания (например, var value = await SomeCall();). - person Chris Hannon; 24.08.2012
comment
Спасибо, отличное объяснение. Теперь это имеет смысл, но изначально уровень абстракции конструкции сбивал меня с толку. - person HolySamosa; 25.08.2012

Во втором примере, который вы дали, вы ничего не вернули. (См. ответ Криса Хэннона, почему).

public static async Task<T> TimeoutAfter<T>(this Task<T> task, int millisecondsTimeout) {
    if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout)))
        return await task; // return the Task of T
    else
        throw new TimeoutException();
}

В дополнение к тому, что сказал @ChrisHannon, из ожидаемой документации.

... если await применяется к результату вызова метода, который возвращает Task<TResult>, то тип выражения ожидания будет TResult. Если await применяется к результату вызова метода, который возвращает Task, то тип выражения ожидания будет void. ...

person David Anderson    schedule 24.08.2012
comment
Спасибо, Дэвид. Я ценю пример. - person HolySamosa; 25.08.2012