Я согласен, что создание оболочки, которая реализует TextReader
, делегирует реализацию базовому TextReader
и добавляет некоторую дополнительную поддержку для отслеживания местоположения - хороший подход. Вы можете думать об этом как о шаблоне Decorator , потому что вы украшаете класс TextReader
некоторыми дополнительными функциями.
Лучшая упаковка: Вы можете написать это более простым способом, чтобы отделить TextReader
от типа Location
- таким образом, вы можете легко изменить Location
независимо или даже предоставить другие функции, которые на основе отслеживания прогресса чтения.
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;
}
}
Я бы также включил создание трекера в некоторый фабричный метод (чтобы у вас было одно место в приложении, которое имеет дело со строительством). Обратите внимание, что вы можете использовать этот простой дизайн для создания TextReader
, который сообщает о прогрессе нескольким Location
объектам:
static TextReader CreateReader(TextReader source, params ITracker[] trackers) {
return trackers.Aggregate(source, (reader, tracker) =>
new TrackingReader(reader, tracker));
}
Это создает цепочку из TrackingReader объектов, и каждый объект сообщает о ходе чтения одному из трекеров, переданных в качестве аргументов.
По поводу событий : я думаю, что использование стандартных событий .NET / C # для такого рода вещей не так часто делается в библиотеках .NET, но я думаю, что этот подход может быть довольно интересным тоже - особенно в C # 3, где вы можете использовать такие функции, как лямбда-выражения или даже Reactive Framework.
Простое использование не добавляет столько плиты котла:
using(ReaderWithEvents reader = new ReaderWithEvents(source)) {
reader.Advance += str => loc.AdvanceString(str);
// ..
}
Однако вы также можете использовать Reactive Extensions для обработки событий. Чтобы подсчитать количество новых строк в исходном тексте, вы можете написать что-то вроде этого:
var res =
(from e in Observable.FromEvent(reader, "Advance")
select e.Count(ch => ch == '\n')).Scan(0, (sum, current) => sum + current);
Это даст вам событие res
, которое срабатывает каждый раз, когда вы читаете какую-то строку из TextReader
, и значение, переносимое событием, будет текущим номером строки (в тексте).