【问题标题】:What's the VB.NET equivalent of async delegate in C#?C# 中异步委托的 VB.NET 等价物是什么?
【发布时间】:2019-05-06 04:26:40
【问题描述】:

我正在尝试将以下扩展方法 (source) 从 C# 转换为 VB:

public static Task ForEachAsync<T>(this IEnumerable<T> source,
                                   int dop, Func<T, Task> body)
{
    return Task.WhenAll(
        from partition in Partitioner.Create(source).GetPartitions(dop)
        select Task.Run(async delegate {
            using (partition)
                while (partition.MoveNext())
                    await body(partition.Current);
        }));
}

delegate 的常规等效项是 Sub(),AFAIK,但我没想到它会在这种情况下工作,因为 Async 关键字(它没有)。所以,我尝试改用Function()

<System.Runtime.CompilerServices.Extension>
Public Function ForEachAsync(Of T)(source As IEnumerable(Of T),
                                   dop As Integer, body As Func(Of T, Task)) As Task
    Return Task.WhenAll(
        From partition In Partitioner.Create(source).GetPartitions(dop)
        Select Task.Run(Async Function() 'As Task '<-- see below.
                            Using partition
                                Do While partition.MoveNext()
                                    Await body(partition.Current)
                                Loop
                            End Using
                        End Function))
End Function

但这仍然无法编译并显示以下错误:

  • WhenAll:

    Overload resolution failed because no accessible 'WhenAll' can be called with these arguments:
        'Public Shared Overloads Function WhenAll(Of TResult)(tasks As IEnumerable(Of Task(Of TResult))) As Task(Of TResult())': Type parameter 'TResult' cannot be inferred.
        'Public Shared Overloads Function WhenAll(Of TResult)(ParamArray tasks As Task(Of TResult)()) As Task(Of TResult())': Type parameter 'TResult' cannot be inferred.
    
  • Await body(partition.Current):

    'Await' 只能在第一个查询表达式中使用 初始 'From' 子句的集合表达式或在 'Join' 子句的集合表达式。

  • [警告]Async Function()(如果我添加As Task,它就会消失)

    函数 '&lt;anonymous method&gt;' 不会在所有代码上返回值 路径。空引用异常可能在运行时发生,当 使用结果。

我做错了什么?在 VB 中执行此操作的正确方法是什么?

【问题讨论】:

  • 您可能可以使用Task.WhenAll(Partitioner.Create(source).GetPartitions(dop).Select(Function(p) (Task.Run(Async Function() Using p ... all the rest ... End Using End Function) 而不是From partition In Partitioner.Create(source).GetPartitions(dop) Select Task.Run(Async Function() ... all the rest ...) 来做到这一点(未经测试)
  • 当然其余的只是While p.MoveNext() Await body(p.Current) End While :)
  • @Jimi 这很奇怪!我不知道为什么我没有想到这一点。但更奇怪的是,即使两个版本都应该做同样的事情(对吗?),它仍然有效。我错过了什么?无论如何,请将此作为答案发布:)
  • 你可能会喜欢这个(因为作者:)Async/Await FAQ。请参阅 我在哪里不能使用“等待”? 部分,最后一点。
  • @Jimi 这基本上与上面第二个错误所说的相同。我不明白的部分是它与 C# 代码有什么不同?另一方面,如果您想发布答案,我会接受,因为您确实帮助我解决了问题。

标签: c# vb.net lambda async-await c#-to-vb.net


【解决方案1】:

在 C# 中,可以使用 delegate type 或使用调用运算符 () 后跟 => 标记作为 lambda 运算符来调用匿名方法来表示异步 Lambda:

Task.Run(async ()=> { } );
Task.Run(async delegate { } );

在 VB.Net 中,可以使用 Lambda 表达式调用匿名方法,使用 Sub()Function(),内联和 Sub / End SubFunction() / End Function 块:

Task.Run(Async Sub() [operation on captured variables])
Task.Run(Sub()
             [operation on captured variables]
         End Sub))

Task.Run(Async Function() [operation on captured variables])
Task.Run(Function()
             Return [operation on captured variables]
         End Function))

VB.Net 的 LINQ to SQL 不允许在 Select 子句中等待,因为:

Await 只能在第一个查询表达式中使用 初始 From 子句的集合表达式或在 Join 子句的集合表达式

Stephen Toub 的 Async/Await FAQ 中引用了它。

Select Task.Run(Async Function() ... ) 尝试返回 IEnumerble(Of TResult) 而不是 IEnumerble(Of Task)

Language-Integrated Query (LINQ) (Visual Basic) 中的更多信息。

相反,LINQ to Objects - 在没有其他中间提供者的情况下使用 IEnumerable/IEnumerable&lt;T&gt; 集合 - 确实允许 Select 方法上的异步/等待模式:

<Extension>
Public Function ForEachAsync(Of T)(source As IEnumerable(Of T), dop As Integer, body As Func(Of T, Task)) As Task
    Return Task.WhenAll(
        Partitioner.Create(source).GetPartitions(dop).
        Select(Function(p) (
                   Task.Run(Async Function()
                                Using p
                                    While p.MoveNext()
                                        Await body(p.Current)
                                    End While
                                End Using
                            End Function))))
End Function

LINQ to SQL 的 C# 版本允许这样做。
为什么,因为同样的规则也应该适用于 C# 实现?

The .NET Language Strategy:

C#

我们将不断发展 C# 以满足开发人员不断变化的需求和 仍然是最先进的编程语言。我们将创新 积极进取,同时非常小心地保持在 语言。

VB.Net

我们将继续专注于跨语言工具体验, 认识到许多 VB 开发人员也使用 C#。我们将专注 在VB流行的核心场景和领域进行创新。

因此,2010 年声明的 VB and C# Coevolution 发生了变化:C#VB.Net 功能更新已解耦。因此,考虑到新的语言策略,VB.NetC# 不再显示大致相同的采用率

【讨论】:

  • 嘿吉米!抱歉错过了 - 恭喜并欢迎来到 10k 俱乐部! :)
  • @Visual Vincent 您好 :) 谢谢。你怎么看待这件事?与 C#(好吧,正确的,旗舰语言和所有......)相比,链接到 Sql 在 VB.Net 实现中的某些部分是否落后?我添加了更多与问题相关的信息。如果您有一些见解,请告诉我
  • 我不太喜欢 Linq,但从我收集到的内容来看,在选择中执行 Await 应该可以在两种语言中正常工作。 OP 收到的错误(“Await may only be used in a query expression (...)”)没有多大意义,因为Await 在@987654353 运行的方法中@ 而不是直接在 Linq 表达式中,这意味着它应该对表达式没有任何影响。看到只是将那段代码移动到一个单独的方法(甚至只是一个变量)工作得很好,在我看来编译器错误在这种情况下是一个错误。
  • 谢谢,吉米!你一直很有帮助。 @VisualVincent 是的,这对我来说没有意义。尽管问题已经通过使用方法语法(我实际上更喜欢)而不是根据 Jimi 的建议使用查询语法来解决,但您的观点正是为什么我在发布问题之前不费心尝试切换到方法语法,因为我从来没有以为会有所作为!
【解决方案2】:

这不是问题的真正答案,但如果它是真正的异步代码,则无需对执行进行分区:

<System.Runtime.CompilerServices.Extension>
Public Function ForEachAsync(Of T)(
    source As IEnumerable(Of T),
    body As Func(Of T, Task)) As Task
    Return Task.WhenAll(        
        From item In source
        Select body(item))
End Function

【讨论】:

  • 我会采用这种方法,但它不会做同样的事情。分区允许您通过将它们作为“块”运行来限制同时运行的任务的数量。想象一下对 10K URL 的集合执行 Web 请求并尝试同时启动 10,000 个任务。
  • 什么是运行时?如果是 .NET Framework,服务点管理器会控制它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-04
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 2012-05-19
  • 1970-01-01
相关资源
最近更新 更多