开发者

Why cannot System.IO.StreamReader read from my custom stream?

I'm in the process of creating a custom stream for an API endpoint in my app. The stream needs to have custom logic that I don't want to get 开发者_运维百科into, but suffice to say I can't use a built-in stream class.

I did the minimum necessary to implement a read-only stream (inheriting from System.IO.Stream) and I've verified that the System.IO.BinaryReader class can read from my stream:

Dim reader As New System.IO.BinaryReader(GenerateStream(business, logic))
Dim enc As New System.Text.ASCIIEncoding
Dim contents As String = enc.GetString(reader.ReadBytes(CType(reader.BaseStream.Length, Int32)))

The string "contents" contains the correct string for the entire stream.

However, I would like to be able allow the use of the System.IO.StreamReader class:

Dim reader As New System.IO.StreamReader(GenerateStream(business, logic), System.Text.Encoding.ASCII)
Dim contents As String = reader.ReadToEnd

but for whatever reason the ReadToEnd always returns the empty string.

Any ideas?

Here's the stream:

Public Overrides ReadOnly Property CanRead() As Boolean
   Get
    Return True
   End Get
  End Property

  Public Overrides ReadOnly Property CanSeek() As Boolean
   Get
    Return False
   End Get
  End Property

  Public Overrides ReadOnly Property CanWrite() As Boolean
   Get
    Return False
   End Get
  End Property

  Public Overrides Sub Flush()
   'this method intentionally left blank'
  End Sub

  Public Overrides ReadOnly Property Length() As Long
   Get
    Return 'some business logic'
   End Get
  End Property

  Public Overrides Property Position() As Long
   Get
    Return bytePosition
   End Get
   Set(ByVal value As Long)
    Throw New System.NotSupportedException
   End Set
  End Property

  Public Overrides Function Read(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) As Integer
   'I return 0 on an end of stream, otherwise the # of bytes successfully read.'
  End Function

  Public Overrides Function Seek(ByVal offset As Long, ByVal origin As System.IO.SeekOrigin) As Long
   Throw New System.NotSupportedException
  End Function

  Public Overrides Sub SetLength(ByVal value As Long)
   Throw New System.NotSupportedException()
  End Sub

  Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
   Throw New System.NotSupportedException()
  End Sub


Looking closely at the description of StreamReader.ReadToEnd shows, quote:

If the initial position within the stream is unknown or the stream does not support seeking, the underlying Stream object also needs to be reinitialized.

It further goes on, saying:

To avoid such a situation and produce robust code you should use the Read method and store the read characters in a pre-allocated buffer.

But, looking under the hood, only Stream.Read is called, it seems, which should provide the output you need. But, you do know that StreamReader reads characters, it tries hard to interpret the data as a string. Not sure what happens if it cannot. What does the data consist of?


Update: Consider the following test code (sorry, C#, but use a converter to get your code), I did not include the non-implemented methods from the abstract class so as to not distract from the core. Just as a proof of concept, this seems to work with ASCII encoding and without the need to re-init or an implementation for Seek.

public class AsciiStream : Stream
{
    private string dataStream = "abcdefghijklmnopqrstuvwxyz";
    private long position = 0;

    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }


    public override long Length
    {
        get { return dataStream.Length; }
    }

    public override long Position
    {
        get
        {
            return position;
        }
        set
        {
            position = value < this.Length ? value : this.Length;
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        long bufferPos = offset;
        long max = this.position + count;
        max = max < this.Length ? max : this.Length;
        for (; this.position < max; this.position++)
        {
            buffer[bufferPos] = Convert.ToByte(this.dataStream[(int) this.position]);
            bufferPos++;
        }

        return (int) bufferPos - offset;
    }
}


// call the code like as follows:
StreamReader sReader = new StreamReader(new AsciiStream(), Encoding.ASCII);
string sToEnd = sReader.ReadToEnd();

Point being: the problem must lie in your actual Read-code or in the business logic you don't show us. The approach you've chosen, on itself, should simply work. Have you tried implementing, by hand, a "read-to-end"? Or with the BinaryReader, reading past the end with ReadBytes(many)``?


Try doing this:

Dim reader As New System.IO.StreamReader(
    GenerateStream(business, logic), 
    System.Text.Encoding.ASCII, 
    False
)
Dim contents As String = reader.ReadToEnd

Also make sure all the methods you don't want used will throw NotImplementedException.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜