【问题标题】:C#5 ReadAsync and IteratorsC#5 ReadAsync 和迭代器
【发布时间】:2012-10-20 23:19:20
【问题描述】:

我正在尝试将以下类转换为延迟返回文件。

public class ObservableFile2 : IObservable<string>
{
    private readonly IObservable<string> subject;

    public ObservableFile2(string fileName)
    {
        subject = Observable.Using<string, StreamReader>
            (
                () => new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)),
                streamReader => ObserveLines(streamReader)
            );
    }

    private IObservable<string> ObserveLines(StreamReader streamReader)
    {
        return ReadLines(streamReader).ToObservable();
    }

    private IEnumerable<string> ReadLines(StreamReader streamReader)
    {
        while (!streamReader.EndOfStream)
        {
            yield return streamReader.ReadLine();
        }
    }        

    public IDisposable Subscribe(IObserver<string> observer)
    {
        return subject.Subscribe(observer);
    }
}

我现在正在尝试将其转换为使用

StreamReader.ReadLineAsync() 

甚至更好的是对数据进行分块,即

await SourceStream.ReadAsync(buffer, 0, chunkSize). 

我似乎没有掌握如何 打包和解包任务

欢迎提供帮助。

谢谢

【问题讨论】:

  • 恕我直言,IEnumerable 可能更适合这种情况。

标签: c# iterator system.reactive async-await c#-5.0


【解决方案1】:

不是 Rx 大师,所以可能有比我的答案更好的方法。

我相信这应该可以使用async-enabled Create

public static class ObservableFile2
{
  public static IObservable<string> Create(string fileName)
  {
    return Observable.Create<string>(async (subject, token) =>
    {
      try
      {
        using (var streamReader = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
          while (true)
          {
            token.ThrowIfCancellationRequested();
            var line = await streamReader.ReadLineAsync();
            if (line == null)
            {
              subject.OnCompleted();
              return;
            }
            subject.OnNext(line);
          }
        }
      }
      catch (Exception ex)
      {
        subject.OnError(ex);
      }
    });
  }
}

【讨论】:

  • 在回答问题时,这个是懒惰的 - 一旦订阅,它将立即读取/OnNext 完整的文件!正如 Asti 提到的 - 如果你想要懒惰,你应该使用 IEnumerable
【解决方案2】:

我不知道您的解决方案是否需要异步功能,因为您没有提到它-我只能看到您想“懒惰地”使用该文件-我的猜测是您想获得一次一行,如果是这样的话,这个应该可以解决问题:

public static IEnumerable<string> EnumerateLines(string fileName)
{
    using (
        var streamReader =
            new StreamReader(new FileStream(fileName,
                                            FileMode.Open,
                                            FileAccess.Read,
                                            FileShare.Read)))
    {
        while (true)
        {
            if (streamReader.EndOfStream)
                yield break;

            Console.WriteLine("read another line...");
            yield return streamReader.ReadLine();
        }
    }
}

请注意,这仅取决于 StreamReader.ReadLine 的实现细节。 您甚至可以尝试使用 Lazy 对这种非严格评估进行评估 - 但随后您会遇到处理文件句柄的麻烦,因为您不知道这些值何时真正被消耗(即使 Haskell 也有这个问题:)) -我的建议:不要试图对文件懒惰 ...这会让你很容易陷入困境

【讨论】:

  • 谢谢,但它确实需要异步,因为一切都将在一个线程中运行。
  • 好的——你认为从文件中读取一行会有问题吗?但是,如果您说这很好,那么 observable 确实可能是一个很好的解决方案 - 而不是一个懒惰的解决方案!如果您只返回 IEnumerable> 并在产生下一个值之前等待任务完成,您仍然可以使用类似的东西 - 当然,您将一直打开文件,您将不得不稍微实现这一点不同(递归 - 异步函数浮现在脑海) - 但它可以完成
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多