I agree that creating a wrapper that implements TextReader delegates the implementation to the underlying TextReader and adds some additional support for location tracking is a good approach. You can think of it as a Decorator pattern because you decorate the TextReader class TextReader some extra features.
Improved packaging:. You can write it in an easier way to separate the TextReader from the Location type - this way you can easily change the Location yourself or even provide other functions that are based on tracking reading progress.
interface ITracker { void AdvanceString(string s); } class TrackingReader : TextReader { private TextReader source; private ITracker tracker; public TrackingReader(TextReader source, ITracker tracker) { this.source = source; this.tracker = tracker; } public override int Read(char[] buffer, int index, int count) { int res = base.Read(buffer, index, count); tracker.AdvanceString(buffer.Skip(index).Take(res); return res; } }
I would also encapsulate the creation of the tracker in some kind of factory method (so that you have one place in the application that deals with construction). Note that you can use this simple TextReader creation, which also reports progress to several Location objects:
static TextReader CreateReader(TextReader source, params ITracker[] trackers) { return trackers.Aggregate(source, (reader, tracker) => new TrackingReader(reader, tracker)); }
This creates a chain of TrackingReader objects, and each of the objects reports the reading progress to one of the trackers passed as arguments.
As for the events . I think that using standard .NET / C # events for this kind of thing is not performed so often in .NET libraries, but I think this approach can be quite interesting as well - especially in C # 3, where you can use such functions like lambda expressions or even the Reactive Framework.
Simple use does not add as much boiler plate:
using(ReaderWithEvents reader = new ReaderWithEvents(source)) { reader.Advance += str => loc.AdvanceString(str);
However, you can also use Reactive Extensions to handle events. To count the number of new lines in the source, you can write something like this:
var res = (from e in Observable.FromEvent(reader, "Advance") select e.Count(ch => ch == '\n')).Scan(0, (sum, current) => sum + current);
This will give you a res event that fires every time you read a line from TextReader , and the value carried by the event will be the current number of lines (in the text).