【问题标题】:Why does await work on multiple lines but not in one line?为什么 await 可以在多行上工作,但不能在一行上工作?
【发布时间】:2021-12-01 07:39:59
【问题描述】:

我可以让以下逻辑在 2 行中运行,但不能内联:

var result = await BuildNewsCategories();
x.ListBuilder = () => result.Select(x => _mapper.Map<a, b>(x)).ToList();

我的第一次尝试只是警告添加异步:

x.ListBuilder = async () => await BuildNewsCategories()
   .Select(x => _mapper.Map<a, b>(x)).ToList();

但这就是我卡住的地方,因为现在找不到.Select

【问题讨论】:

  • 加括号:(await BuildNewsCategories())
  • 根据经验,您应该在所有async 的方法后面加上“Async”一词。如果BuildNewsCategories 实际上是异步的,为了清楚起见,它应该命名为BuildNewsCategoriesAsync
  • ListBuilder 属性的类型是什么?

标签: c# async-await syntax


【解决方案1】:

x.ListBuilder 是同步委托,因此您不能将其更改为单线。

无论如何都应该避免阻塞。


即使你要阻止,例如:

x.ListBuilder = () => BuildNewsCategories()
   .Result
   .Select(_mapper.Map<a, b>)
   .ToList();

这会改变你的代码的语义,因为现在BuildNewsCategories() 被评估每次委托被调用。

【讨论】:

  • 我可以用 Task.Run 和/或匿名函数做些什么棘手的事情。由于我们的缓存是如何工作的,那 1 行之外的任何东西都更昂贵。
  • 在不了解更广泛背景的情况下,我无法提供进一步的建议。根据您共享的代码,并假设 BuildNewsCategories 具有真正的异步实现,您的第一个示例是最佳解决方案。
  • 它也可能导致死锁,具体取决于使用此代码的位置
【解决方案2】:

TLDR:不,你不能把它变成一个可用的单行,因为你有一个同步委托

此答案的其余部分为搜索此问题时可能出现的其他问题提供了额外的信息和潜在的解决方案。


await BuildNewsCategories() 周围没有括号意味着编译器无法知道:

  • 你想在Task&lt;TResult 上做.Select 吗? 或者:
  • 您是否尝试对Task (TResult) 的结果执行.Select

如果没有括号,它认为您正在尝试在返回的 Task 而不是实际返回的结果(“未包装”awaited 结果)上调用 LINQ .Select 扩展方法。

要对Task 的结果执行.Select,请将await BuildNewsCategories() 括在括号中:


如果x.ListBuilderList&lt;T&gt; 类型:

x.ListBuilder = (await BuildNewsCategories())
                .Select(x => _mapper.Map<a, b>(x))
                .ToList()

如果x.ListBuilderFunc&lt;Task&lt;List&lt;T&gt;&gt;&gt; 类型:

x.ListBuilder = async () => (await BuildNewsCategories())
                            .Select(x => _mapper.Map<a, b>(x))
                            .ToList();

如果 x.ListBuilderFunc&lt;List&lt;T&gt;&gt; 类型,则您的委托是同步的,如果不阻塞异步代码(违背目的),您将无法获得单行代码。

在这种情况下,您的原始解决方案将是答案:

var result = await BuildNewsCategories();
x.ListBuilder = () => result
                      .Select(x => _mapper.Map<a, b>(x))
                      .ToList();

【讨论】:

  • @RobertHarvey async 是不必要的。问题是 BuildNewsCategories() 似乎是一个 async 方法,但 OP 实际上并没有等待任务的结果,所以他们的代码试图在任务本身上调用 LINQ 的 Select,而不是结果。
  • 但 ListBuilder 似乎是问题中的委托(或操作)类型,而不是列表。
  • x.ListBuilder 是委托类型。看看第一个编译的例子。
  • 每次编辑都会使您的答案变得更糟。真正的答案是你不能在一行上做,除非你做BuildNewsCategories().GetAwaiter().GetResult()....,但那太可怕了,应该避免。
  • 唯一真正的答案是:做不到。问题中发布的代码表明 ListBuilder 是一个同步委托,因此不能使用 await,这使得这个答案的大部分内容无关紧要。我不知道这是怎么得到这么多赞成的
【解决方案3】:

其他答案正确解决了括号的问题,但不返回委托,而是返回列表。

所以这将是正确的代码:

x.ListBuilder = async () => (await BuildNewsCategories())
             .Select(x => _mapper.Map<a, b>(x)).ToList();

如果x.ListBuilder 的类型为Func&lt;Task&lt;List&lt;b&gt;&gt;&gt;,这将起作用,因此可以将其称为var list = await x.ListBuilder()。如果它必须是Func&lt;List&lt;b&gt;&gt;,则不能异步调用它,您必须在单独的语句中移动await BuildNewsCategories()(您的第一个sn-p)。

【讨论】:

  • 我也尝试包装它,但后来它变得混乱..错误出现在箭头下方,说它无法将异步 lambda 表达式转换为委托类型 Func>
  • @JBoothUA 你能把 ListBuilder 改成 Func>> 吗?如果没有,那么你必须坚持你的两行解决方案。
  • @JBoothUA Func&lt;List&lt;b&gt;&gt; 是同步委托,因此您无法将代码转换为单行代码。
猜你喜欢
  • 2017-03-12
  • 1970-01-01
  • 2017-03-10
  • 1970-01-01
  • 1970-01-01
  • 2014-10-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多