如何正确使用 "await using" 语法?

10

我有以下同步代码,它可以正常工作:

    private void GenerateExportOutput()
    {
        using StreamWriter writer = new(Coordinator.OutputDirectory + @"\export.txt");

        if (this.WikiPagesToExport.IsEmpty)
        {
            return;
        }

        var wanted = new SortedDictionary<string, WikiPage>(this.WikiPagesToExport, StringComparer.Ordinal);
        foreach (var title in wanted.Keys)
        {
            writer.WriteLine(title);
        }
    }

我想将其更改为异步。因此:

    private async Task GenerateExportOutputAsync()
    {
        using StreamWriter writer = new(Coordinator.OutputDirectory + @"\export.txt");

        if (this.WikiPagesToExport.IsEmpty)
        {
            return;
        }

        var wanted = new SortedDictionary<string, WikiPage>(this.WikiPagesToExport, StringComparer.Ordinal);
        foreach (var title in wanted.Keys)
        {
            await writer.WriteLineAsync(title).ConfigureAwait(false);
        }

        await writer.FlushAsync().ConfigureAwait(false);
    }

代码可编译通过。但我使用的分析器之一 (Meziantou.Analyzer) 建议我“更喜欢使用 'await using'”。虽然以前尝试过几次,总是遇到同样的问题,但我仍想使用它,所以:

        await using StreamWriter writer = new StreamWriter(OutputDirectory + @"\export.txt").ConfigureAwait(false);

现在它不能再编译:CS0029 无法将类型 'System.Runtime.CompilerServices.ConfiguredAsyncDisposable' 隐式转换为 'System.IO.StreamWriter'。好的,那么我使用 var 进行修改:

        await using var writer = new StreamWriter(OutputDirectory + @"\export.txt").ConfigureAwait(false);

这样可以避免CS0029错误,但后面的代码现在无法编译:Error CS1061“ConfiguredAsyncDisposable”不包含“WriteLineAsync”的定义FlushAsync也有类似的错误)。那怎么办呢?也许需要进行强制转换?

            await ((StreamWriter)writer).WriteLineAsync(title).ConfigureAwait(false);

错误: Error CS0030 无法将类型“System.Runtime.CompilerServices.ConfiguredAsyncDisposable”转换为类型“System.IO.StreamWriter”

我已经通过搜索和阅读了大量的材料,无论是现在还是过去几次,但我仍然完全不知道如何使用“await using”。我该如何做?谢谢。


2
https://www.tabsoverspaces.com/233779-using-await-using-iasyncdisposable-with-configureawait - madreflection
2
再来一个以确保万无一失... https://github.com/dotnet/csharplang/discussions/2661 - madreflection
using var writer = new StreamWriter("..."); then on the next line: await writer.WriteLineAsync().ConfigureAwait(false); - Charles
1
@Charles:这可能可以(指不会抛出异常),但它不会调用流对象的 IAsyncDisposable.Dispose 方法,而是使用 IDisposable.Dispose 方法。 - madreflection
1
@Charles:当然,除非你不能直接在那上面使用ConfigureAwait(false),这是这个问题的关键。我链接的页面展示了在将某些东西分配给_variable_之后使用await using (_variable_.ConfigureAwait(false))。该变量具有正确的类型,而单独使用await using则捕获用于异步处理的配置等待对象。 - madreflection
显示剩余3条评论
2个回答

17

await using 语法目前(C# 10)在配置等待 IAsyncDisposable 方面仍有很大的不足之处。我们能做到的最好的就是这样:

private async Task GenerateExportOutputAsync()
{
    StreamWriter writer = new(Coordinator.OutputDirectory + @"\export.txt");
    await using (writer.ConfigureAwait(false))
    {
        //...
    }
}

......这种语法与不使用await using语法相比并没有更加紧凑:

private async Task GenerateExportOutputAsync()
{
    StreamWriter writer = new(Coordinator.OutputDirectory + @"\export.txt");
    try
    {
        //...
    }
    finally { await writer.DisposeAsync().ConfigureAwait(false); }
}

相关的 GitHub 问题: 在 "await using" 声明中使用 ConfigureAwait


1
这可以用两行代码完成:
private async Task GenerateExportOutputAsync()
{
    StreamWriter writer = new(Coordinator.OutputDirectory + @"\export.txt");
    await using var _ = writer.ConfigureAwait(false);
    //...
}

2
这将创建一个名为_的变量。它不是discard。如果您尝试在同一方法中嵌套另一个await using var _ =,则会因为重复声明变量_而导致编译错误。 - Theodor Zoulias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接