Yield in VB.NET

.Netvb.net

.Net Problem Overview


C# has the keyword called yield. VB.NET lacks this keyword. How have the Visual Basic programmers gotten around the lack of this keyword? Do they implement they own iterator class? Or do they try and code to avoid the need of an iterator?

The yield keyword does force the compiler to do some coding behind the scenes. The implementation of iterators in C# and its consequences (part 1) has a good example of that.

.Net Solutions


Solution 1 - .Net

Note: This answer is old now. Iterator blocks have since been added to VB.NET

C# translates the yield keyword into a state machine at compile time. VB.NET does not have the yield keyword, but it does have its own mechanism for safely embedding state within a function that is not easily available in C#.

The C# static keyword is normally translated to Visual Basic using the Shared keyword, but there are two places where things get confusing. One is that a C# static class is really a Module in Visual Basic rather than a Shared class (you'd think they'd let you code it either way in Visual Basic, but noooo). The other is that VB.NET does have its own Static keyword. However, Static has a different meaning in VB.NET.

You use the Static keyword in VB.NET to declare a variable inside a function, and when you do the variable retains its state across function calls. This is different than just declaring a private static class member in C#, because a static function member in VB.NET is guaranteed to also be thread-safe, in that the compiler translates it to use the Monitor class at compile time.

So why write all this here? Well, it should be possible to build a re-usable generic Iterator<T> class (or Iterator(Of T) in VB.NET). In this class you would implement the state machine used by C#, with Yield() and Break() methods that correspond to the C# keywords. Then you could use a static instance (in the VB.NET sense) in a function so that it can ultimately do pretty much the same job as C#'s yield in about the same amount of code (discarding the class implemenation itself, since it would be infinitely re-usable).

I haven't cared enough about Yield to attempt it myself, but it should be doable. That said, it's also far from trivial, as C# team member Eric Lippert calls this "the most complicated transformation in the compiler."

I have also come to believe since I wrote the first draft of this over a year ago that it's not really possible in a meaningful way until Visual Studio 2010 comes out, as it would require sending multiple lambdas to the Iterator class and so to be really practical we need .NET 4's support for multi-line lambdas.

Solution 2 - .Net

The Async CTP includes support for Yield in VB.NET.

See Iterators in Visual Basic for information on usage.

And now it's included in the box with Visual Studio 2012!

Solution 3 - .Net

There's the nice article Use Iterators in VB Now by Bill McCarthy in Visual Studio Magazine on emulating yield in VB.NET. Alternatively wait for the next version of Visual Basic.

Solution 4 - .Net

Fortunately now we have Yield return
here is an example from my project + implementing an interface with System.Collections.Generic.IEnumerable(T) function:

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

This is how i extract all properties from outside:

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

Solution 5 - .Net

I personally just write my own iterator class that inherits from IEnumerator(Of T). It does take some time to get it right, but I think in the end it's better to write it right then try to avoid it. Another method that I have done is to write a recursive method that returns IEnumerable(Of T) and just returns List(Of T) and uses .AddRange.

Solution 6 - .Net

VB.NET has the Iterator keyword https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/modifiers/iterator

Since Visual Studio 2012 it seems

Solution 7 - .Net

Hopefully, this will be a thing of the past with the upcoming version of VB. Since iterators are actually gaining a lot of importance with new paradigms (especially LINQ in combination with lazy evaluation), this has quite a high priority, as far as I know from Paul Vick's blog. Then again, Paul's no longer the head of the VB team and I haven't yet had time to watch the PCD talks.

Still, if you're interested, they're linked in Paul's blog.

Solution 8 - .Net

The below code gives the output

2, 4, 8, 16, 32

In 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

In C#

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;
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMagicKatView Question on Stackoverflow
Solution 1 - .NetJoel CoehoornView Answer on Stackoverflow
Solution 2 - .NetCoderDennisView Answer on Stackoverflow
Solution 3 - .NetMarkJView Answer on Stackoverflow
Solution 4 - .NetJonathan ApplebaumView Answer on Stackoverflow
Solution 5 - .NetMagicKatView Answer on Stackoverflow
Solution 6 - .NetLuke T O'BrienView Answer on Stackoverflow
Solution 7 - .NetKonrad RudolphView Answer on Stackoverflow
Solution 8 - .Netmeenakshisundaram muthukrishnaView Answer on Stackoverflow