Что такое VB.NET-эквивалент асинхронного делегата в C #? - PullRequest
2 голосов
/ 06 мая 2019

Я пытаюсь преобразовать следующий метод расширения ( 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):

    «Ожидание» может использоваться только в выражении запроса в пределах первого выражения коллекции исходного «От»или в выражении коллекции предложения «Join».

  • [Предупреждение] в Async Function(): (оно исчезнет, ​​если я добавлю As Task)

    Функция '<anonymous method>' не возвращает значение для всех путей кода.При использовании результата может возникать исключение нулевой ссылки.

Что я делаю не так?И как правильно сделать это в VB?

Ответы [ 2 ]

3 голосов
/ 06 мая 2019

В C # асинхронная лямбда может быть выражена с типом делегата или с помощью оператора вызова () , за которым следует маркер => как лямбдаоператор для вызова анонимного метода:

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

VB.Net использование анонимного метода может быть вызвано с помощью лямбда-выражения с использованием Sub () или 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, поскольку:

Ожидание может использоваться только в выражении запроса в пределахпервое выражение коллекции исходного предложения From или в выражении коллекции предложения Join

На него есть ссылки в Async / Await FAQ Стивена Туба .

Select Task.Run(Async Function() ... ) пытается вернуть IEnumerble(Of TResult) вместо IEnumerble(Of Task).

Подробнее в Интегрированный в язык запрос (LINQ) (Visual Basic) .

И наоборот, LINQ to Objects - работа с IEnumerable/IEnumerable<T> коллекциями без других промежуточных провайдеров - разрешает использование асинхронного / ожидающего паттерна для метода 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 #?
Расследование .......

Языковая стратегия .NET :

C # :

Мы будем продолжать расширять C #, чтобы удовлетворять растущие потребности разработчиков и оставаться в состояниихудожественный язык программирования.Мы будем активно вводить новшества, при этом стараясь быть в рамках духа языка.

VB.Net :

Мы будем сохранятьсосредоточиться на опыте работы с разными языками, признавая, что многие разработчики VB также используют C #.Мы сосредоточим инновации на основных сценариях и областях, где VB популярен.

Таким образом, VB и C # Coevolution , заявленные в 2010 году, сместились: C# и VB.Netобновление функций было отделено.Следовательно, учитывая новую языковую стратегию, VB.Net и C# не показывают примерно одинаковое принятие .

0 голосов
/ 06 мая 2019

Не совсем ответ на вопрос, но, если это действительно асинхронный код, нет необходимости разбивать выполнение:

<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
...