Esiste un blocco StreamReader, TextReader o StringReader in .NET?
Domanda
Voglio eseguire un'attività in background che legge l'input da un TextReader e lo elabora una riga alla volta. Voglio che l'attività in background si blocchi fino a quando l'utente digita del testo in un campo e fa clic sul pulsante di invio. C'è un po 'di sapore di TextReader che si bloccherà fino a quando il testo non sarà disponibile e ti permetterà in qualche modo di aggiungere altro testo alla fonte sottostante?
Ho pensato che uno StreamReader e StreamWriter che puntavano allo stesso MemoryStream potrebbero funzionare, ma non sembra. StreamReader rileva che MemoryStream è vuoto all'inizio e non verifica mai più.
Mi rendo conto che sarebbe più semplice scrivere un metodo ProcessLine () e chiamarlo ogni volta che l'utente fa clic sul pulsante di invio. Tuttavia, sto cercando di progettare un'architettura plug-in e vorrei che i plug-in sembrassero come app console vecchio stile con un flusso di input e un flusso di output. Voglio che il flusso di input del plug-in si blocchi solo finché l'utente non fa clic sul pulsante di invio con del testo di input.
Soluzione
Sembra che non ci sia alcuna implementazione di questo - il che è strano, dal momento che sono d'accordo che sarebbe un costrutto utile. Ma dovrebbe essere semplice da scrivere. Qualcosa del genere dovrebbe funzionare:
public class BlockingStream: Stream
{
private readonly Stream _stream;
public BlockingStream(Stream stream)
{
if(!stream.CanSeek)
throw new ArgumentException("Stream must support seek", "stream");
_stream = stream;
}
public override void Flush()
{
lock (_stream)
{
_stream.Flush();
Monitor.Pulse(_stream);
}
}
public override long Seek(long offset, SeekOrigin origin)
{
lock (_stream)
{
long res = _stream.Seek(offset, origin);
Monitor.Pulse(_stream);
return res;
}
}
public override void SetLength(long value)
{
lock (_stream)
{
_stream.SetLength(value);
Monitor.Pulse(_stream);
}
}
public override int Read(byte[] buffer, int offset, int count)
{
lock (_stream)
{
do
{
int read = _stream.Read(buffer, offset, count);
if (read > 0)
return read;
Monitor.Wait(_stream);
} while (true);
}
}
public override void Write(byte[] buffer, int offset, int count)
{
lock (_stream)
{
long currentPosition = _stream.Position;
_stream.Position = _stream.Length;
_stream.Write(buffer, offset, count);
_stream.Position = currentPosition;
Monitor.Pulse(_stream);
}
}
public override bool CanRead
{
get
{
lock (_stream)
{
return _stream.CanRead;
}
}
}
public override bool CanSeek
{
get
{
lock (_stream)
{
return _stream.CanSeek;
}
}
}
public override bool CanWrite
{
get
{
lock (_stream)
{
return _stream.CanWrite;
}
}
}
public override long Length
{
get
{
lock (_stream)
{
return _stream.Length;
}
}
}
public override long Position
{
get
{
lock (_stream)
{
return _stream.Position;
}
}
set
{
lock (_stream)
{
_stream.Position = value;
Monitor.Pulse(_stream);
}
}
}
}
Altri suggerimenti
Penso che sarebbe molto meglio creare un evento nella tua applicazione principale che viene generato quando l'utente preme Invia. I dati di testo sarebbero passati negli argomenti dell'evento. Ogni plug-in registra un gestore eventi per l'evento e gestisce i dati trasmessi quando viene generato l'evento. Ciò consente a molti plug-in di elaborare i dati da un singolo invio senza molto lavoro idraulico da parte tua e significa che i plug-in sono in grado di rimanere inattivi fino a quando non viene generato l'evento.