views:

481

answers:

4

Guys, I'm trying to convert something from C# to VB.NET and I'm having trouble finding an equivlent in VB.NET to C#'s yield keyword. I realize 'yield' is not a convertable keyword to VB.NET, so can someone please show me how I would implement this code in VB.NET. I got all of it converted over except for the implemented GetEnumerator() function. It is simply a class that implements CollectionBase and IEnumerable (to make it LINQ worthy):

[Serializable()]
public partial class Customers : CollectionBase,    System.Collections.Generic.IEnumerable<BusinessLayer.Customer> 
{

    public new System.Collections.Generic.IEnumerator<BusinessLayer.Customer> GetEnumerator()
    {
        foreach (BusinessLayer.Customer Cust in this.List)
        {
            yield return Cust;
        }
    }

    public Customers()
    {

 }

 public Customers(DataRowCollection datarows) : this()
 {
  this.Load(datarows);
 }
    protected void Load(DataRowCollection dataRows)
{
 foreach (DataRow dr in dataRows) {
  this.Add(new Customer(dr));
 }
}

public Customer this[int index] {
 get { return (Customer)base.InnerList[index]; }
 set { base.InnerList[index] = value; }
}

public int Add(Customer val)
{
 return base.InnerList.Add(val);
}
}

Thanks in advance for any help!

+5  A: 

Because you cannot use the yield keyword, you will have to implement GetEnumerator() another way. What you can do is return the enumerator of the List from CollectionBase. However, because this is an IList and not an IList<T>, you will have to cast it (you can use the Cast<T>() extension method from Linq for this). Your C# code then becomes:

public IEnumerator<BusinessLayer.Customer> GetEnumerator()
{
    return InnerList.Cast<BusinessLayer.Customer>().GetEnumerator();
}

This gives the same result, but behaves in a slightly different way (with regards to no longer using the delayed execution of yield).

In VB.Net, GetEnumerator() would be:

Public Function GetEnumerator() As IEnumerator(Of BusinessLayer.Customer)
    Return InnerList.Cast(Of BusinessLayer.Customer)().GetEnumerator()
End Function

The rest of your code should translate directly to VB.Net.

adrianbanks
Thanks Adrian. This looks like it works!
icemanind
+1  A: 

Either wait for the next version of VB.NET, or consult this nice article by Bill McCarthy in Visual Studio magazine.

MarkJ
A: 

I know it's been a while since you asked the question, but I was having the same problem of not having a 'yield' keyword in VB. This is what I found and have been using in its stead. You can implement 'yield' with a GenericIterator, here is code for such an Iterator:

    Public Class GenericIterator(Of T)
    Implements IEnumerable(Of T)
    Implements IEnumerator(Of T)

    Public Delegate Function MoveNextFunc(ByRef nextItem As T) As Boolean

    Private _Current As T
    Private _func As MoveNextFunc

    Public Sub New(ByVal func As MoveNextFunc)
        _func = func
    End Sub


    Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
        Return _func(_Current)
    End Function


    Public Function GetEnumerator() As IEnumerator(Of T) Implements IEnumerable(Of T).GetEnumerator
        Static iBeenCalled As Int32
        If (iBeenCalled = 0) AndAlso _
           (Threading.Interlocked.Increment(iBeenCalled) = 1) Then
            Return Me
        Else
            Return New GenericIterator(Of T)(_func)
        End If
    End Function


    Public ReadOnly Property Current() As T Implements IEnumerator(Of T).Current
        Get
            Return _Current
        End Get
    End Property

    Public Overridable Sub Reset() Implements IEnumerator.Reset
        Throw New NotImplementedException("Iterator cannot be reset")
    End Sub


    Private Function IEnumerator_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return Me.GetEnumerator
    End Function

    Private ReadOnly Property IEnumerator_Current() As Object Implements IEnumerator.Current
        Get
            Return Me.Current
        End Get
    End Property

    Public Sub Dispose() Implements IDisposable.Dispose
        ' not implemented
    End Sub

End Class

Once you have this class you can implement 'yield' similar to how it's done in this sample Zip extension function:

Public Module IEnumerableExtensions

    <Extension()>
    Public Function Zip(Of T1, T2)(ByVal left As IEnumerable(Of T1), ByVal right As IEnumerable(Of T2)) As IEnumerable(Of Pair(Of T1, T2))
        Dim leftG As IEnumerator(Of T1) = left.Select(Function(x) x).GetEnumerator()
        Dim rightG As IEnumerator(Of T2) = right.Select(Function(x) x).GetEnumerator()
        Return New GenericIterator(Of Pair(Of T1, T2)) _
            (Function(ByRef x) As Boolean
                 Dim canMove As Boolean = leftG.MoveNext() AndAlso rightG.MoveNext()
                 x = New Pair(Of T1, T2)(leftG.Current, rightG.Current)
                 Return canMove
             End Function)
    End Function

End Module

The lambda function has a single by ref parameter which you are supposed to save your return value to.

It is worth noting that I called the selects on leftG and rightG because the compiler was complaining about how some IEumerables like {"foo", "bar"} had already materialized. The select calls naturally removed the materialization. It is also worth noting that for the extension function to work properly you need to import System.Runtime.CompilerServices. For the Iterator you need System.Collections.Generic. And in general you obviously need System.Linq. It is also worth noting that in the above function 'Pair' is:

Public Class Pair(Of T1, T2)
    Public Property First As T1
    Public Property Second As T2

    Public Sub New(ByVal f As T1, ByVal s As T2)
        First = f
        Second = s
    End Sub
End Class

I hope this helps your conversion efforts in some way! Good luck!

diceguyd30
I appreciate the answer. Adrian's answer worked for what I needed though.
icemanind
Cool, I'm glad it all worked out.
diceguyd30