'What's the VB.NET equivalent of async delegate in C#?
I'm trying to convert the following extension method (source) from C# to 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);
}));
}
The regular equivalent of delegate
is Sub()
, AFAIK, but I didn't expect it to work in this situation because of the Async
keyword (and it didn't). So, I tried using Function()
instead:
<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
But this still doesn't compile and shows the following errors:
At
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.
At
Await body(partition.Current)
:'Await' may only be used in a query expression within the first collection expression of the initial 'From' clause or within the collection expression of a 'Join' clause.
[Warning] at
Async Function()
: (it goes away if I addAs Task
)Function '
<anonymous method>
' doesn't return a value on all code paths. A null reference exception could occur at run time when the result is used.
What am I doing wrong? And what is the correct way to do this in VB?
Solution 1:[1]
In C#, the async Lambda can be expressed with a delegate type or using the invocation operator () followed by the => token as a lambda operator to invoke an anonymous method:
Task.Run(async ()=> { } );
Task.Run(async delegate { } );
In VB.Net, an anonymous method can be invoked with a Lambda expression using Sub()
or Function()
, both in-lined and with Sub
/ End Sub
, Function()
/ End Function
blocks:
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's LINQ to SQL doesn't allow to await in a Select
clause, because:
Await may only be used in a query expression within the first collection expression of the initial
From
clause or within the collection expression of aJoin
clause
It's referenced in Stephen Toub's Async/Await FAQ.
Select Task.Run(Async Function() ... )
tries to return an IEnumerable(Of TResult)
instead of an IEnumerable(Of Task)
.
More in Language-Integrated Query (LINQ) (Visual Basic).
Conversely, LINQ to Objects - working with IEnumerable/IEnumerable<T>
collections without other intermediate providers - does allow the async/await patter on a Select
method:
<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
The C# version of LINQ to SQL allows it instead.
Why, since the same rule should also apply to the C# implementation?
C#:
We will keep growing C# to meet the evolving needs of developers and remain a state of the art programming language. We will innovate aggressively, while being very careful to stay within the spirit of the language.
VB.Net:
We will keep a focus on the cross-language tooling experience, recognizing that many VB developers also use C#. We will focus innovation on the core scenarios and domains where VB is popular.
Thus, the VB and C# Coevolution asserted in 2010 has shifted: C#
and VB.Net
features update has been decoupled. Hence, given the new language strategy, VB.Net
and C#
don't show roughly equal adoption anymore.
Solution 2:[2]
Not really the answer to the question but, if it's truly asynchronous code, there's no need for partitioning the execution:
<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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Visual Vincent |