使用带有 yield 关键字和 MemoryStream 的 Enumerable 方法

问题描述

我写了下面的代码,它有效:

//VERSION 1;
static IEnumerable<string> ReadAsLines(string filename)
{
    using (StreamReader reader = new StreamReader(filename))
    {
        while (!reader.EndOfStream)
            yield return reader.ReadLine();
    }
}

使用上述方法

const string fileData = @"path\to\somePipeDelimitedData.txt";
var reader = ReadAsLines(fileData); 
var headerArr = reader.First().Split('|');
foreach (var column in headerArr)
{
    var dummy = column;
}
var recordsEnumerable = reader.Skip(1); //skip first header Line
//Read other lines...
foreach (var record in recordsEnumerable)
{
    //read each line
    var rowArray = record.Split('|');
    //etc...
}

现在假设我从一个 Stream 而不是一个文件开始; 我尝试重新编写上面的代码,但正在为关闭流而苦苦挣扎。 如何修复以下版本?

//VERSION 2;
static IEnumerable<string> ReadAsLines(Stream stream)
{
    using (StreamReader reader = new StreamReader(stream))
    {
        while (!reader.EndOfStream)
            yield return reader.ReadLine();
    }
}

调用版本 2:

byte[] dataByteArr = File.ReadAllBytes(fileData);
MemoryStream memStr = new MemoryStream(dataByteArr);

var reader2 = ReadAsLines(memStr);
var headerArr2 = reader2.First().Split('|'); //*** STREAM gets closed after this line
foreach (var column in headerArr2)
{
    var dummy = column;
}

var recordsEnumerable2 = reader2.Skip(1); //skip first header Line

//Read other lines... *** ERROR OCCURS HERE,as the Stream is closed.
foreach (var record in recordsEnumerable2)
{
    //read each line
    var rowArray = record.Split('|');
    //etc...
}

解决方法

我重新组织了我最初的尝试,将 StreamReader 从 Enumerable 方法中提取出来,并在我真正完成后将其处理掉。

byte[] dataByteArr = File.ReadAllBytes(fileData); //decoded bytes
var memStr = new MemoryStream(dataByteArr);

using (StreamReader sr = new StreamReader(memStr))
{
    var dataAsEnumerable = ReadAsLines(sr,memStr);
    var headerArr2 = dataAsEnumerable.First().Split('|'); 
    //*** HA! stream is still open !
    foreach (var column in headerArr2)
    {
        var dummy = column;
    }
    var dataMinusHeader = dataAsEnumerable.Skip(1);

    //Read other lines...
    foreach (var record in dataMinusHeader)
    {
        //read each line
        var rowArray = record.Split('|');
        //etc...
    }
}