marc leclair
marc leclair

Reputation: 3

Having issues bypassing yield in vb.net

(the whole chunk of code is at the bottom of the page)

Let me start by prefacing that I'm aware there is yield ( from what I saw) in VB 2015. However my work forces me to code from 2010. So... I have to find a work around. My code uses Jon Skeet answer on how to read a file "bottom up" ( in c#) : How to read a text file reversely with iterator in C#

The code works fine - except where yield is. So, instead of "yielding", I added the string to a List of String i.e:

   If firstYield AndAlso String.IsNullOrEmpty(previousEnd) Then

                Return results
            End If
            If (previousEnd IsNot Nothing) Then
               'yield was here
                results.Add(previousEnd)


            Else
                results.Add("")
                'yield was here
                Return results
            End If

            Return results
       Finally
            For i As Integer = 0 To results.Count - 1
                Console.WriteLine(results(i))
            Next

            stream.Dispose()

Running this code I get this error: Unable to cast object of type 'System.Collections.Generic.List`1[System.String]' to type 'System.Collections.Generic.IEnumerator'1[System.String]

Now, I do understand I'm trying to return a list of String to a method that is suppose to return an enumerator of Type string. But I am at a lost of what to do to make my code work, I tried to do a return of array such as:

results.ToArray()

which doesn't work because my initial method can't return an array as it's implementing : Implements System.Collections.Generic.IEnumerable(Of String).GetEnumerator and VS is throwing me all sorts of error when I try (maybe I'm writing the syntax wrong, I'm rather new to VB). However, when I just use the console.Writeline, my whole file copies correctly in my result List. So my only issues is returning the List of string correctly.

Here's the 2 methods ( very lengthy, hence why I explained my problem before):

   Private Function GetEnumeratorImpl(stream As Stream) As IEnumerator(Of String)
        Dim results As New List(Of String)
        Try
            Dim position As Long = stream.Length

            If TypeOf encoding Is UnicodeEncoding AndAlso (position And 1) <> 0 Then
                Throw New InvalidDataException("UTF-16 encoding provided, but stream has odd length.")
            End If

            ' Allow up to two bytes for data from the start of the previous
            ' read which didn't quite make it as full characters
            Dim buffer__1 As Byte() = New Byte(bufferSize + 1) {}
            Dim charBuffer As Char() = New Char(encoding.GetMaxCharCount(buffer__1.Length) - 1) {}
            Dim leftOverData As Integer = 0
            Dim previousEnd As [String] = Nothing
            ' TextReader doesn't return an empty string if there's line break at the end
            ' of the data. Therefore we don't return an empty string if it's our *first*
            ' return.
            Dim firstYield As Boolean = True

            ' A line-feed at the start of the previous buffer means we need to swallow
            ' the carriage-return at the end of this buffer - hence this needs declaring
            ' way up here!
            Dim swallowCarriageReturn As Boolean = False

            While position > 0
                Dim bytesToRead As Integer = Math.Min(If(position > Integer.MaxValue, bufferSize, CInt(position)), bufferSize)

                position -= bytesToRead
                stream.Position = position
                StreamUtil.ReadExactly(stream, buffer__1, bytesToRead)
                ' If we haven't read a full buffer, but we had bytes left
                ' over from before, copy them to the end of the buffer
                If leftOverData > 0 AndAlso bytesToRead <> bufferSize Then
                    ' Buffer.BlockCopy doesn't document its behaviour with respect
                    ' to overlapping data: we *might* just have read 7 bytes instead of
                    ' 8, and have two bytes to copy...
                    Array.Copy(buffer__1, bufferSize, buffer__1, bytesToRead, leftOverData)
                End If
                ' We've now *effectively* read this much data.
                bytesToRead += leftOverData

                Dim firstCharPosition As Integer = 0
                While Not characterStartDetector(position + firstCharPosition, buffer__1(firstCharPosition))
                    firstCharPosition += 1
                    ' Bad UTF-8 sequences could trigger this. For UTF-8 we should always
                    ' see a valid character start in every 3 bytes, and if this is the start of the file
                    ' so we've done a short read, we should have the character start
                    ' somewhere in the usable buffer.
                    If firstCharPosition = 3 OrElse firstCharPosition = bytesToRead Then
                        Throw New InvalidDataException("Invalid UTF-8 data")
                    End If
                End While
                leftOverData = firstCharPosition

                Dim charsRead As Integer = encoding.GetChars(buffer__1, firstCharPosition, bytesToRead - firstCharPosition, charBuffer, 0)
                Dim endExclusive As Integer = charsRead

                For i As Integer = charsRead - 1 To 0 Step -1
                    Dim lookingAt As Char = charBuffer(i)
                    If swallowCarriageReturn Then
                        swallowCarriageReturn = False
                        If lookingAt = ControlChars.Cr Then
                            endExclusive -= 1
                            Continue For
                        End If
                    End If
                    ' Anything non-line-breaking, just keep looking backwards
                    If lookingAt <> ControlChars.Lf AndAlso lookingAt <> ControlChars.Cr Then
                        Continue For
                    End If
                    ' End of CRLF? Swallow the preceding CR
                    If lookingAt = ControlChars.Lf Then
                        swallowCarriageReturn = True
                    End If
                    Dim start As Integer = i + 1
                    Dim bufferContents As New String(charBuffer, start, endExclusive - start)
                    endExclusive = i
                    Dim stringToYield As String = If(previousEnd Is Nothing, bufferContents, bufferContents & previousEnd)
                    If Not firstYield OrElse stringToYield.Length <> 0 Then
                        results.Add(stringToYield)
                    End If
                    firstYield = False
                    previousEnd = Nothing
                Next

                previousEnd = If(endExclusive = 0, Nothing, (New String(charBuffer, 0, endExclusive) & previousEnd))

                ' If we didn't decode the start of the array, put it at the end for next time
                If leftOverData <> 0 Then
                    Buffer.BlockCopy(buffer__1, 0, buffer__1, bufferSize, leftOverData)
                End If
            End While
            If leftOverData <> 0 Then
                ' At the start of the final buffer, we had the end of another character.
                Throw New InvalidDataException("Invalid UTF-8 data at start of stream")
            End If
            If firstYield AndAlso String.IsNullOrEmpty(previousEnd) Then

                Return results
            End If
            If (previousEnd IsNot Nothing) Then
                results.Add(previousEnd)


            Else
                results.Add("")
                Return results
            End If

            Return results

        Finally
            For i As Integer = 0 To results.Count - 1
                Console.WriteLine(results(i))
            Next

            stream.Dispose()

        End Try

    End Function

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

    Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of String) Implements System.Collections.Generic.IEnumerable(Of String).GetEnumerator
        Dim stream As Stream = streamSource()

        If Not stream.CanSeek Then
            stream.Dispose()
            Throw New NotSupportedException("Unable to seek within stream")
        End If

        If Not stream.CanRead Then

            stream.Dispose()
            Throw New NotSupportedException("Unable to read within stream")

        End If
        Return GetEnumeratorImpl(stream)
    End Function

Upvotes: 0

Views: 69

Answers (1)

Sage Pourpre
Sage Pourpre

Reputation: 10333

Well, according to your error and the reference link you provided, you simply need to return the enumerator instead of the list itself. To do so, you need to change

Else
    results.Add("")
    'yield was here
    Return results
End If

for

Else
    results.Add("")
    'yield was here
    Return results.GetEnumerator()
End If

Your function return does expect the ienumerator and not the list, hence why you get this error.

Upvotes: 1

Related Questions