Насколько я понимаю, если у вас есть метод async, в котором единственным await является окончательный возврат Task, вы можете удалить ключевые слова async и await и просто верните Task.

Например:

public async Task<object> Handle(object message)
{
    var result = await Task.FromResult(message);
    return result;
}

Становится

public Task<object> Handle(object message)
{
    return Task.FromResult(message);
}

Однако это не работает, если метод содержит несколько операторов ожидания.

Например:

async Task Main()
{
    // outputs: Request
    var requestHandlerAwaitingResult = (Request) await new HandlerAwaitingResult().Handle(new Request());
    Debug.WriteLine(requestHandlerAwaitingResult.Description);

    // InvalidCastException: Unable to cast object of type 'System.Threading.Tasks.Task`1[System.Object]' to type 'Request'.
    var requestHandlerReturnTask = (Request) await new HandlerReturningTask().Handle(new Request());
    Debug.WriteLine(requestHandlerReturnTask.Description);
}

public class Request
{
    public string Description = "Request";
}

public class HandlerAwaitingResult
{
    public async Task<object> Handle(object message)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        var result = await Task.FromResult(message);
        return result;
    }
}

public class HandlerReturningTask
{
    public async Task<object> Handle(object message)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        return Task.FromResult(message);
    }
}

Кто-нибудь может сказать мне, почему это не работает?

По сути, в чем разница между

public class HandlerReturningTask
{
    public async Task<object> Handle(object message)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        return Task.FromResult(message);
    }
}

А также

public class HandlerReturningTask
{
    public async Task<object> Handle(object message)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        return await Task.FromResult(message);
    }
}

А также

public class HandlerReturningTask
{
    public Task<object> Handle(object message)
    {
        return Task.FromResult(message);
    }
}
1
kimsagro 28 Апр 2016 в 06:54

2 ответа

Лучший ответ

Я все еще не уверен, что понимаю вопрос. Но на основе вашего редактирования:

По сути, в чем разница между [… return Task.FromResult(message);…] и [… return await Task.FromResult(message);…]

Важно понимать, что делает await: он представляет собой точку в вашем методе, из которой метод может возвращаться, а затем выполнение в методе может возобновиться позже, когда "ожидающий" (например, Task) завершится .

Если Task - это Task<T>, то другая вещь, которую делает await, - это при возобновлении выполнения в методе разворачивать значение T для объекта Task<T>. , т.е. получить значение его свойства Result. Выражение await вычисляет это значение.

Наконец, для методов async Task<T> оператор return заставляет объект Task<T>, возвращаемый методом (в первом выражении await), иметь значение Result в Выражение return {value}.

Итак, в вашем примере return Task.FromResult(message); заставляет свойство Task<object>.Result иметь значение объекта типа Task<Request>. Позже выражение await оценивает значение свойства этого объекта Result, и вы пытаетесь преобразовать его в объект типа Request, что, конечно, является незаконным.

Использование return await Task.FromResult(message); приводит к тому, что сначала вычисляется выражение await (поэтому оператор return может возвращать результат этого выражения), что приводит к получению значения свойства Task<Request>.Result . Это значение затем возвращается оператором return, в результате чего ожидаемое значение Task<object>.Result становится объектом Request, первоначально переданным методу. Это, конечно, может быть приведено обратно к типу Request при разворачивании await в вызывающей программе.

Тем не менее, обычно вы просто возвращаете значение напрямую. Бессмысленно заключать его в объект Task<T> только для того, чтобы сразу же развернуть его с помощью выражения await. Написание return message; в этом сценарии даст точно такой же результат, но с более читаемым и более эффективным кодом.

1
Peter Duniho 28 Апр 2016 в 04:48

Сгенерированный конечный автомат обрабатывает все ожидаемые. Не только задачи.

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

Тип возврата асинхронных методов всегда будет задачей того, что вы возвращаете.

0
Paulo Morgado 28 Апр 2016 в 05:46