返回任务与返回等待结果时的异常

时间:2016-04-28 03:54:18

标签: c# asynchronous async-await

我的理解是,如果您使用async方法,await最终返回Task,则可以删除async和{{1 }}关键字,只返回await

例如:

Task

变为

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

但是,当方法包含多个await语句时,这似乎不起作用。

例如:

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);
    }
}

2 个答案:

答案 0 :(得分:1)

我仍然不确定我理解这个问题。但根据你的编辑:

  

从根本上说,[... return Task.FromResult(message); ...]和[... return await Task.FromResult(message);之间有什么区别...]

了解await的作用非常重要:它代表方法可以返回的方法中的一个点,然后在“等待”时可以在方法中执行(例如{{1} })完成。

如果TaskTask,那么Task<T>做的另一件事是,在方法中恢复执行时,打开{{1}的await值object,即获取其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>将获得完全相同的结果,但代码更具可读性和效率。

答案 1 :(得分:0)

生成的状态机处理任何等待的。不仅是任务。

如果您只处理一项任务,则可以绕过计算机生成的状态机的开销。但是,在异常的情况下,堆栈跟踪将不是您所期望的。

异步方法的返回类型将始终是您返回的任务的任务。