【问题标题】:Warning message in IAsyncEnumerable when it lacks the await operatorIAsyncEnumerable 中缺少 await 运算符时的警告消息
【发布时间】:2020-01-14 00:00:50
【问题描述】:

当调用这样的方法而不执行await 任务时,我们可以返回以下内容:

public Task<bool> GetBoolAsync()
{
    return Task.FromResult(true);
}

IAsyncEnumerable&lt;&gt; 的等效项并避免警告。

async IAsyncEnumerable<bool> GetBoolsAsync() // <- Ugly warning
{
    yield return true;
    yield break;
}

警告 CS1998 此异步方法缺少“等待”运算符,将同步运行。考虑使用 'await' 运算符来等待非阻塞 API 调用,或使用 'await Task.Run(...)' 在后台线程上执行 CPU 密集型工作。

【问题讨论】:

  • 如果你的方法不使用等待,为什么它是异步的?这就是警告的意思。
  • 您打算在该方法中做任何异步操作吗?
  • 它正在匹配一个接口。其他类将使其异步。
  • 那好吧,不要注意警告。
  • 这里没有必要忽略警告 - 根据我的回答,代码可以很容易地编写为无警告。 (没有明显的原因,它被否决了,但其他答案也被否决了。代码肯定有效。)

标签: c# async-await iasyncenumerable


【解决方案1】:

我可能会编写一个同步迭代器方法,然后使用System.Linq.Async 包中的ToAsyncEnumerable 将其转换为异步版本。这是一个完整的例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        await foreach (bool x in GetBoolsAsync())
        {
            Console.WriteLine(x);
        }
    }

    // Method to return an IAsyncEnumerable<T>. It doesn't
    // need the async modifier.
    static IAsyncEnumerable<bool> GetBoolsAsync() =>
        GetBools().ToAsyncEnumerable();

    // Regular synchronous iterator method.
    static IEnumerable<bool> GetBools()
    {
        yield return true;
        yield break;
    }
}

这符合接口(使用IAsyncEnumerable&lt;T&gt;),但允许同步实现,没有警告。请注意,async 修饰符本身不是方法签名的一部分——它是一个实现细节。因此,接口中指定的方法返回 Task&lt;T&gt;IAsyncEnumerable&lt;T&gt; 或任何可以用异步方法实现的方法,但不一定非要如此。

当然,对于只想返回单个元素的简单示例,您可以在数组上使用ToAsyncEnumerable,或者使用Enumerable.Repeat 的结果。例如:

static IAsyncEnumerable<bool> GetBoolsAsync() =>
    new[] { true }.ToAsyncEnumerable();

【讨论】:

【解决方案2】:

我认为您正在寻找TaskCompletionSource。这为您提供了最大的灵活性。您可以在任务上设置例外,将其标记为未完成等。或者Task.FromResult

在很多场景中,启用一个Task来表示是很有用的 外部异步操作。 TaskCompletionSource 是 为此目的而提供。它允许创建一个任务,该任务可以 分发给消费者。消费者可以使用会员 以与在任何其他场景处理任务中相同的方式执行任务 成员变量。

考虑以下两个选项,我包括第一个,但你不能返回 Task.CompletedTask&lt;T&gt;,但如果你只想返回一个已完成的任务,例如因为你可能有一些 out 参数已经设置,那么这可能就是你的答案。

    static async Task SomeMethod()
    {
        await Task.CompletedTask;
    }

或者 - 因为我们不能在没有异步的情况下使用等待

    static Task<bool> SomeOtherMethod()
    {
        var taskSource = new TaskCompletionSource<bool>();
        taskSource.SetResult(true);
        return taskSource.Task;
    }

或(.Net 4.5+)

   static Task<bool> SomeEvenOtherMethod()
    {
        return Task.FromResult(true);
    }

或上述的异步变体

    static async Task<bool> SomeEvenOtherMethod()
    {
        var taskSource = new TaskCompletionSource<bool>();
        taskSource.SetResult(true);
        return (await taskSource.Task);
    }

或者:(.Net 4.5+)

    static async Task<bool> SomeEvenOtherMethod()
    {
        return(await Task.FromResult(true));
    }

还有一个 IAsyncEnumerable

    static async IAsyncEnumerable<bool> GetBoolsAsync()
    {
        var t = await SomeOtherMethod();
        yield return t;
    }

或(.Net 4.5+)

    static async IAsyncEnumerable<bool> GetBoolsAsync() 
    {
        yield return await Task.FromResult(true);
    }

【讨论】:

  • 我认为您的解决方案是可以接受的,但是,我的return Task.FromResult(true); 通常没有等待。我不知道TaskCompletionSource,如果我将它与我的IAsyncEnumerable 混合,我不会调用SomeOtherMethod(),而是将TaskCompletionSource 的想法复制到我的IAsyncEnumerable 中。它有效,但删除警告非常冗长。感谢您的反馈。
  • 是的,它只是 4.5 之前的版本。我假设您知道您可以全局关闭警告,甚至可以按方法关闭警告。 docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
猜你喜欢
  • 2014-02-13
  • 2021-01-24
  • 1970-01-01
  • 2015-07-07
  • 1970-01-01
  • 2020-04-04
  • 2018-10-21
  • 1970-01-01
  • 2013-05-16
相关资源
最近更新 更多