Search code examples
c#streamreader

Override StreamReader's ReadLine method


I'm trying to override a StreamReader's ReadLine method, but having difficulty doing so due to inability to access some private variables. Is this possible, or should I just write my own StreamReader class?


Solution

  • Assuming you want your custom StreamReader to be usable anywhere that a TextReader can be used there are typically two options.

    1. Inherit from StreamReader and override the functions that you want to have work differently. In your case this would be StreamReader.ReadLine.

    2. Inherit from TextReader and implement the reader functionality completely to your requirements.

    NB: For option 2 above, you can maintain an internal reference to a StreamReader instance and delegate all the functions to the internal instance, except for the piece of functionality that you want to replace. In my view, this is just an implementation detail of option 2 rather than a 3rd option.

    Based on your question I assume you have tried option 1 and found that overriding StreamReader.ReadLine is rather difficult because you could not access the internals of the class. Well for StreamReader you are lucky and can achieve this without having access to the internal implementation of the StreamReader.

    Here is a simple example:

    Disclaimer: The ReadLine() implementation is for demonstration purposes and is not intended to be a robust or complete implementation.

    class CustomStreamReader : StreamReader
    {
      public CustomStreamReader(Stream stream)
        : base(stream)
      {
      }
    
      public override string ReadLine()
      {
        int c;
    
        c = Read();
        if (c == -1)
        {
          return null;
        }
    
        StringBuilder sb = new StringBuilder();
        do
        {
          char ch = (char)c;
          if (ch == ',')
          {
            return sb.ToString();
          }
          else
          {
            sb.Append(ch);
          }
        } while ((c = Read()) != -1);
        return sb.ToString();
      }
    }
    

    You will notice that I simply used the StreamReader.Read() method to read the characters from the stream. While definitely less performant that working directly with the internal buffers, the Read() method does use the internal buffering so should still yield pretty good performance, but that should be tested to confirm.

    For fun, here is a example of option 2. I used the encapsulated StreamReader to reduce the actual code, this is not tested at all..

    class EncapsulatedReader : TextReader
    {
      private StreamReader _reader;
    
      public EncapsulatedReader(Stream stream)
      {
        _reader = new StreamReader(stream);      
      }
    
      public Stream BaseStream
      {
        get
        {
          return _reader.BaseStream;
        }
      }
    
      public override string ReadLine()
      {
        int c;
    
        c = Read();
        if (c == -1)
        {
          return null;
        }
        StringBuilder sb = new StringBuilder();
        do
        {
          char ch = (char)c;
          if (ch == ',')
          {
            return sb.ToString();
          }
          else
          {
            sb.Append(ch);
          }
        } while ((c = Read()) != -1);
        return sb.ToString();
      }
    
      protected override void Dispose(bool disposing)
      {
        if (disposing)
        {
          _reader.Close();
        }
        base.Dispose(disposing);
      }
    
      public override int Peek()
      {
        return _reader.Peek();
      }
    
      public override int Read()
      {
        return _reader.Read();
      }
    
      public override int Read(char[] buffer, int index, int count)
      {
        return _reader.Read(buffer, index, count);
      }
    
      public override int ReadBlock(char[] buffer, int index, int count)
      {
        return _reader.ReadBlock(buffer, index, count);
      }
    
      public override string ReadToEnd()
      {
        return _reader.ReadToEnd();
      }
    
      public override void Close()
      {
        _reader.Close();
        base.Close();
      }
    }