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



我的理解是,如果你有一个async方法,其中唯一的await是最终返回Task,你可以删除asyncawait关键字,只返回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);
}

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

例如:

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

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

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

了解await的作用很重要:它表示方法中的一个点,该方法可以返回,然后当"可等待"(例如Task)完成时,该方法中的执行可以恢复。

如果TaskTask<T>,那么await要做的另一件事是在方法中恢复执行时,解开Task<T>对象的T值,即获取其Result属性值。await表达式的计算结果为该值。

最后,对于async Task<T>方法,return 语句会导致方法返回的Task<T>对象(在第一个await表达式处)在return {value}表达式中具有Result值。

因此,在您的示例中,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对象。当然,当调用者中的await解开包装时,这可以转换回类型 Request

也就是说,通常你只会直接返回值。将其包装在 Task<T> 对象中,然后立即用await表达式将其解开包装是没有意义的。在这种情况下编写return message;将具有完全相同的结果,但代码更具可读性和效率。

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

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

异步方法的返回类型将始终是要返回的任何内容的任务。

最新更新