提问者:小点点

惰性加载从using语句获得的列表


我正在使用CSVHelper库读取CSV文件。但这不是你想要的

请参阅以下代码

public class Reader
{
    public IEnumerable<CSVModel> Read(string file)
    {
       using var reader = new StreamReader(@"C:\Users\z0042d8s\Desktop\GST invoice\RISK All RISKs_RM - Copy.CSV");
       using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
       IEnumerable<CSVModel> records = csv.GetRecords<CSVModel>();
       return records;
    }
}

上面方法中的CSV.getRecords使用yield返回,并在每一个CSV行被读取后立即返回,而不是等到整个CSV文件被读取后再返回(从CSV流数据)

我有一个consumer类,顾名思义,它使用Read方法返回的数据。


class Consumer
{
   public void Consume(IEnumerable<CSVModel> data)
   {
      foreach(var item in data)
      {
         //Do whatever you want with the data. I am gonna log it to the console
         Console.WriteLine(item);
      }
}


下面是调用方

   public static void main()
   {
      var data = new Reader().Read();
      new Consumer().Consume();
   }

希望我没有失去你。

我面临的问题就在下面

由于上面的数据变量是IEnumerable的,所以它将被惰性加载(换句话说,只要不迭代,它就不会读取CSV文件)。但是,当我调用consumer()方法时,它对数据变量进行迭代,强制读取Read()方法中的CSV文件,using语句中的读取器和CSV对象将被释放,引发ObjectDisported异常。

此外,我不想删除using块之外的reader和csv对象,因为它们应该被处理以防止内存泄漏。

异常消息如下

    System.ObjectDisposedException: 'GetRecords<T>() returns an IEnumerable<T> 
    that yields records. This means that the method isn't actually called until 
    you try and access the values. e.g. .ToList() Did you create CsvReader inside 
    a using block and are now trying to access the records outside of that using 
    block?

而且我知道我可以使用贪婪运算符(.toList())。但我想让懒惰的装入工作。

请建议是否有任何出路。

提前道谢。


共1个答案

匿名用户

您可以将操作作为参数传递给读取器。它稍微改变了方法的思想:

public class Reader
{
    public void Read(string file, Action<CSVModel> action)
    {
        using var reader = new StreamReader(@"C:\Users\z0042d8s\Desktop\GST invoice\RISK All RISKs_RM - Copy.CSV");
        using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
        
        IEnumerable<CSVModel> records = csv.GetRecords<CSVModel>();

        foreach(var record in records){
            action(record);
        }
    }
}

class Consumer
{
    public void Consume(CSVModel data)
    {
        //Do whatever you want with the data. I am gonna log it to the console
        Console.WriteLine(item);
    }
}

public static void Main()
{
    var consumer = new Consumer();
    new Reader().Read(consumer.Consume); // Pass the action here 
}

或者,您可以使整个Reader类都是一次性的:

public class Reader : IDisposable
{
    private readonly StreamReader _reader;
    private readonly CsvReader _csv;

    public Reader(string file)
    {
        _reader = new StreamReader(file);
        _csv = new CsvReader(_reader, CultureInfo.InvariantCulture);
    }

    public IEnumerable<CSVModel> Read()
    {
        return csv.GetRecords<CSVModel>();                   
    }

    public void Dispose() => Dispose(true);

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
           _csv.Dispose();
           _reader.Dispose();
        }

        _disposed = true;
    }

}

class Consumer
{
    public void Consume(IEnumerable<CSVModel> data)
    {
        foreach(var item in data)
        {
             //Do whatever you want with the data. I am gonna log it to the console
             Console.WriteLine(item);
        }
    }
}

public static void Main()
{
    using var myReader = new Reader("c:\\path.csv");
    var consumer = new Consumer().Consume(myReader.Read());
}