从Stream </string>生成IObservable <string>的首选方法

时间:2012-04-12 02:20:19

标签: c# system.reactive reactive-programming

作为我们应用程序的一部分(现在生产大约4个月),我们有一个来自外部设备的数据流,我们将其转换为IObservable

到目前为止,我们一直在使用以下方法来生成它,并且它一直运行良好。

IObservable<string> ObserveStringStream(Stream inputStream)
{
    var streamReader = new StreamReader(inputStream);
    return Observable
            .Create<string>(observer => Scheduler.ThreadPool
            .Schedule(() => ReadLoop(streamReader, observer)));
}

private void ReadLoop(StreamReader reader, IObserver<string> observer)
{
    while (true)
    {
        try
        {
            var line = reader.ReadLine();
            if (line != null)
            {
                observer.OnNext(line);
            }
            else
            {
                observer.OnCompleted();
                break;
            }
        }
        catch (Exception ex)
        {
            observer.OnError(ex);
            break;
        }
    }
}

昨晚我想知道是否有办法使用yield return语法来实现相同的结果并想出了这个:

IObservable<string> ObserveStringStream(Stream inputStream)
{
    var streamReader = new StreamReader(inputStream);
    return ReadLoop(streamReader)
            .ToObservable(Scheduler.ThreadPool);
}

private IEnumerable<string> ReadLoop(StreamReader reader)
{
    while (true)
    {
        var line = reader.ReadLine();
        if (line != null)
        {
            yield return line;
        }
        else
        {
            yield break;
        }
    }
}

它似乎工作得很好而且更干净,但我想知道是否有任何优点或缺点,而不是另一种方式,或者是否有更好的方式。

3 个答案:

答案 0 :(得分:13)

我认为你在那里有个好主意(将Stream变成Enumerable<string>然后IObservable<string>)。但是, IEnumerable 代码可以更清晰:

IEnumerable<string> ReadLines(Stream stream)
{
    using (StreamReader reader = new StreamReader(stream))
    {
        while (!reader.EndOfStream)
            yield return reader.ReadLine();
    }
}

然后是 IObservable

IObservable<string> ObserveLines(Stream inputStream)
{
    return ReadLines(inputStream).ToObservable(Scheduler.ThreadPool);
}

这更短,更易读,并且正确处理流。它也很懒惰。

ToObservable扩展程序负责捕获OnNext个事件(新行)以及OnCompleted事件(可枚举结束)和OnError

答案 1 :(得分:2)

我没有代码可以提供,但是这里是如何做到异步预异步CTP。

[对于脱脂阅读器的注意事项:如果你不需要扩展太多就不需要打扰]

创建一个AsyncTextReader实现,它本身就是Observable。 ctor接收一个Stream,并在流上执行BeginRead(256bytes),将自身作为延续,然后返回。

进入延续时,调用EndRead,并将返回的字节添加到类的一个小缓冲区中。重复此操作直到缓冲区包含一个或多个行结束序列(根据TextWriter)。发生这种情况时,通过Observable接口将缓冲区的那些位作为字符串发送出来,然后重复。

完成后,发出OnComplete等信号......(并处理流)。如果从延续中的EndReadByte中抛出异常,请捕获它并将其传递给OnError接口。

调用代码然后看起来像:

  

IObservable = new AsyncTextReader(stream);

这很好。只需要确保你不要对缓冲区处理做任何麻烦。

伪代码:

public ctor(Stream stream){
    this._stream = stream;
    BeginRead();
    return;
}

private void BeginRead(){
    // kick of async read and return (synchronously)
    this._stream.BeginRead(_buffer,0,256,EndRead,this);
}

private void EndRead(IAsyncResult result){
    try{
        // bytesRead will be *up to* 256
        var bytesRead = this._stream.EndRead(result);
        if(bytesRead < 1){
            OnCompleted();
            return;
        }
        // do work with _buffer, _listOfBuffers
        // to get lines out etc...
        OnNext(aLineIFound); // times n
        BeginRead(); // go round again
    }catch(Exception err){
        OnException(err);
    }
}

好的,这是APM,只有母亲才能爱。我非常期待等待替代方案。

ps:读者是否应关闭流是一个有趣的问题。我说不,因为它没有创造它。

答案 2 :(得分:0)

使用async / await支持,以下内容最有可能是您最好的选择:

IObservable<string> ObserveStringStream(Stream inputStream)
{
    return Observable.Using(() => new StreamReader(inputStream), 
        sr => Observable.Create<string>(async (obs, ct) =>
        {
            while (true)
            {
                ct.ThrowIfCancellationRequested();
                var line = await sr.ReadLineAsync().ConfigureAwait(false);
                if (line == null)
                    break;
                obs.OnNext(line);
            }
            obs.OnCompleted();
    }));
}