Reputation: 3
(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
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