Выход в VB.NET

С# имеет ключевое слово yield. VB.NET не хватает этого ключевого слова. Как программисты на Visual Basic воспользовались отсутствием этого ключевого слова? Они реализуют свой класс итераторов? Или они пытаются и кодируют, чтобы избежать необходимости итератора?

yield ключевое слово заставляет компилятор делать некоторую кодировку за кулисами. Хорошим примером этого является реализация итераторов в С# и его последствиях (часть 1).

Ответ 1

Примечание. Этот ответ уже устарел. С тех пор блоки итераторов были добавлены в VB.NET

С# переводит ключевое слово yield в конечный автомат во время компиляции. VB.NET не имеет ключевого слова yield, но у него есть собственный механизм для безопасного встраивания состояния в функцию, которая нелегко доступна в С#.

Ключевое слово С# static обычно переводится в Visual Basic с помощью ключевого слова Shared, но есть два момента, где все становится запутанным. Во-первых, статический класс С# на самом деле является модулем в Visual Basic, а не классом Shared (можно подумать, что они позволят вам кодировать его в любом случае в Visual Basic, но это не так). Другое дело, что VB.NET имеет свое собственное ключевое слово Static. Однако Static имеет другое значение в VB.NET.

Вы используете ключевое слово Static в VB.NET, чтобы объявить переменную внутри функции, и когда вы это делаете, переменная сохраняет свое состояние при вызове функции. Это отличается от простого объявления закрытого статического члена класса в С#, поскольку член статической функции в VB.NET гарантированно также является поточно-ориентированным, поскольку компилятор преобразует его для использования класса Monitor во время компиляции.

Так зачем писать все это здесь? Ну, должно быть возможно создать универсальный класс Iterator<T> многократного использования (или Iterator(Of T) в VB.NET). В этом классе вы реализуете конечный автомат, используемый С#, с методами Yield() и Break(), которые соответствуют ключевым словам С#. Затем вы можете использовать статический экземпляр (в смысле VB.NET) в функции, чтобы в конечном итоге он мог выполнять почти ту же работу, что и С# yield, примерно в том же объеме кода (отбрасывая саму реализацию класса, поскольку будет бесконечно многократно использоваться).

Я недостаточно заботился о Yield, чтобы попробовать это сам, но это должно быть выполнимо. Тем не менее, это также далеко не тривиально, так как член команды С# Эрик Липперт называет это "самым сложным преобразованием в компиляторе".

Я также пришел к выводу, что с тех пор, как я написал первый черновой вариант этого более года назад, до тех пор, пока не выйдет Visual Studio 2010, это не представляется возможным, поскольку это потребует отправки нескольких лямбда-выражений в класс Iterator и, таким образом, будет действительно на практике нам нужна поддержка .NET 4 для многострочных лямбд.

Ответ 2

Async CTP включает поддержку Yield в VB.NET.

Для получения информации об использовании см. Итераторы в Visual Basic.

И теперь он включен в коробку с Visual Studio 2012!

Ответ 3

Там хорошая статья Использование итераторов в VB Now Билла МакКарти в журнале Visual Studio Magazine по эмуляции yield в VB.NET. В качестве альтернативы дождитесь следующей версии Visual Basic.

Ответ 4

К счастью, теперь у нас есть Yield return
Вот пример из моего проекта + реализация интерфейса с функцией System.Collections.Generic.IEnumerable(T):

Public Class Status
    Implements IStatus

    Private _statusChangeDate As DateTime
    Public Property statusChangeDate As DateTime Implements IStatus.statusChangeDate
        Get
            Return _statusChangeDate
        End Get
        Set(value As Date)
            _statusChangeDate = value
        End Set
    End Property

    Private _statusId As Integer
    Public Property statusId As Integer Implements IStatus.statusId
        Get
            Return _statusId
        End Get
        Set(value As Integer)
            _statusId = value
        End Set
    End Property

    Private _statusName As String
    Public Property statusName As String Implements IStatus.statusName
        Get
            Return _statusName
        End Get
        Set(value As String)
            _statusName = value
        End Set
    End Property

    Public Iterator Function GetEnumerator() As IEnumerable(Of Object) Implements IStatus.GetEnumerator
        Yield Convert.ToDateTime(statusChangeDate)
        Yield Convert.ToInt32(statusId)
        Yield statusName.ToString()
    End Function

End Class

Public Interface IStatus
    Property statusChangeDate As DateTime
    Property statusId As Integer
    Property statusName As String
    Function GetEnumerator() As System.Collections.Generic.IEnumerable(Of Object)
End Interface

Вот как я извлекаю все свойства снаружи:

For Each itm As SLA.IStatus In outputlist
    For Each it As Object In itm.GetEnumerator()
        Debug.Write(it & " ")
    Next
    Debug.WriteLine("")
Next

Ответ 5

Лично я просто пишу свой собственный класс итераторов, который наследуется от IEnumerator(Of T). Требуется некоторое время, чтобы понять это правильно, но я думаю, что в конце лучше написать это правильно, а затем попытаться избежать этого. Другой метод, который я сделал, - это написание рекурсивного метода, который возвращает IEnumerable(Of T) и просто возвращает List(Of T) и использует .AddRange.

Ответ 6

Надеюсь, что это будет в прошлом с предстоящей версией VB. Поскольку итераторы на самом деле приобретают большое значение с новыми парадигмами (особенно LINQ в сочетании с ленивой оценкой), это имеет довольно высокий приоритет, насколько мне известно из блога Пола Вика. Опять же, Пол больше не является главой команды VB, и я еще не успел посмотреть переговоры по PCD.

Тем не менее, если вам интересно, они связаны в Paul blog.

Ответ 7

Ниже приведен код выхода

2, 4, 8, 16, 32

В VB.NET,

Public Shared Function setofNumbers() As Integer()
    Dim counter As Integer = 0
    Dim results As New List(Of Integer)
    Dim result As Integer = 1
    While counter < 5
        result = result * 2
        results.Add(result)
        counter += 1
    End While
    Return results.ToArray()
End Function

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    For Each i As Integer In setofNumbers()
        MessageBox.Show(i)
    Next
End Sub

В С#

private void Form1_Load(object sender, EventArgs e)
{
    foreach (int i in setofNumbers())
    {
        MessageBox.Show(i.ToString());
    }
}

public static IEnumerable<int> setofNumbers()
{
    int counter=0;
    int result=1;
    while (counter < 5)
    {
        result = result * 2;
        counter += 1;
        yield return result;
    }
}