【问题标题】:Looking for an elegant way to get around "Cannot await in the body of a finally clause"寻找一种优雅的方式来解决“不能在 finally 子句的主体中等待”
【发布时间】:2014-04-18 22:12:05
【问题描述】:

我有以下功能:

private async Task DoSomething(NamespaceConnectionInfo nci)
{
    var session = await m_sessionProvider.GetSessionAsync(nci);
    SomeLegacySynchronousCode(session);
    await m_sessionProvider.EndSessionAsync(session);
}

EndSessionAsync 记录并吞下任何异常(就像一个好的析构函数)。

问题是SomeLegacySynchronousCode 可能会抛出异常,然后会话泄漏。

我很清楚为什么以下代码是非法的:

private async Task DoSomething(NamespaceConnectionInfo nci)
{
    var session = await m_sessionProvider.GetSessionAsync(nci);
    try
    {
        SomeLegacySynchronousCode(session);
    }
    finally
    {
        await m_sessionProvider.EndSessionAsync(session);
    }
}

所以,我正在寻找一种既正确又优雅的替代方案。

变体 I

private async Task DoSomething(NamespaceConnectionInfo nci)
{
    var session = await m_sessionProvider.GetSessionAsync(nci);
    Exception exc = null;
    try
    {
        SomeLegacySynchronousCode(session);
    }
    catch (Exception e)
    {
        exc = e;
    }
    await m_sessionProvider.EndSessionAsync(session);
    if (exc != null)
    {
        // Wrap to preserve the original stack trace.
        throw new AggregateException(exc);
    }
}

变体 II

private Task DoSomething(NamespaceConnectionInfo nci)
{
    return m_sessionProvider.GetSessionAsync(nci).ContinueWith(t =>
    {
        Task result = null;
        try
        {
            SomeLegacySynchronousCode(t.Result);
        }
        finally
        {
            if (t.Exception == null)
            {
                result = m_sessionProvider.EndSessionAsync(t.Result);
            }
        }
        return result;
    }).Unwrap();
}

两者都不如前面提到的非法 async/await 版本那样优雅。

我希望改进我提出的两个变体,因为坦率地说,两者都很丑。

有什么想法吗?

【问题讨论】:

    标签: c# async-await


    【解决方案1】:

    commonly-accepted answer 似乎与您的变体 1 相似:

    您可以将逻辑移到 catch 块之外并重新抛出 之后的异常,如果需要,使用 ExceptionDispatchInfo。

    static async Task f()
    {
        ExceptionDispatchInfo capturedException = null;
        try
        {
            await TaskThatFails();
        }
        catch (MyException ex)
        {
            capturedException = ExceptionDispatchInfo.Capture(ex);
        }
    
        if (capturedException != null)
        {
            await ExceptionHandler();
    
            capturedException.Throw();
        }
    }
    

    这样,当调用者检查异常的 StackTrace 属性,它仍然会记录它在 TaskThatFails 中被抛出的位置。

    【讨论】:

    • 错过了那个问题。 ExceptionHandler 是什么?我在 MSDN 中看到的是 System.ServiceModel.DispatcherSystem.Reflection.Emit 命名空间。似乎没有一个是相关的。
    • ExceptionHandler() 只是您的异常处理逻辑的占位符。在您的情况下,它将是 m_sessionProvider.EndSessionAsync(session);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多