IEnumerable <string> System.ObjectDisposedException

On some machines, but not on others, I get a System.ObjectDisposedException using this class.

 class LogComparer { private string firstFile; private string secondFile; private IEnumerable<string> inFirstNotInSecond; private IEnumerable<string> inSecondNotInFirst; public LogComparer(string firstFile, string secondFile) { if (!File.Exists(firstFile) || !File.Exists(secondFile)) { throw new ArgumentException("Input file location is not valid."); } this.firstFile = firstFile; this.secondFile = secondFile; GenerateDiff(); } public string FirstFile { get { return firstFile; } } public bool IsEqual { get { return inFirstNotInSecond.SequenceEqual(inSecondNotInFirst); } } public string SecondFile { get { return secondFile; } } public IEnumerable<string> InFirstNotInSecond { get { return inFirstNotInSecond; } } public IEnumerable<string> InSecondNotInFirst { get { return inSecondNotInFirst; } } private void GenerateDiff() { var file1Lines = File.ReadLines(firstFile); var file2Lines = File.ReadLines(secondFile); inFirstNotInSecond = file1Lines.Except(file2Lines); inSecondNotInFirst = file2Lines.Except(file1Lines); } } 
 System.ObjectDisposedException: Cannot read from a closed TextReader. ObjectName: at System.IO.__Error.ReaderClosed() at System.IO.StreamReader.ReadLine() at System.IO.File.<InternalReadLines>d__0.MoveNext() at System.Linq.Enumerable.<ExceptIterator>d__99`1.MoveNext() at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate) 

After changing GenerateDiff() to:

 private void GenerateDiff() { var file1Lines = File.ReadLines(firstFile).ToList(); var file2Lines = File.ReadLines(secondFile).ToList(); inFirstNotInSecond = file1Lines.Except(file2Lines); inSecondNotInFirst = file2Lines.Except(file1Lines); } 

I cannot throw an exception.

Interestingly, this does not work:

 private void GenerateDiff() { var file1Lines = File.ReadLines(firstFile); var file2Lines = File.ReadLines(secondFile); inFirstNotInSecond = file1Lines.Except(file2Lines).ToList(); inSecondNotInFirst = file2Lines.Except(file1Lines).ToList(); } 

I am using an instance of this diff class here, for example. No using or Dispose anywhere. No tasks or threads.

 if (diff.InSecondNotInFirst.Any(s => !s.Contains("bxsr"))) 

Can someone explain the reason? Thanks.

(We assume that, because of IEnumerable<> implements lazy loading, and the garbage collector closes the reader before I want to access InFirstNotInSecond or InSecondNotInFirst . But with GC.Collect() there is still no exception on some machines. )

+5
source share
3 answers

Using the source code , we see that File.ReadLines returns ReadLinesIterator .

And here you can see what Dispose after listing.

This means that enumeration with File.ReadLines can happen only once. It is better to use File.ReadAllLines , which will first enumerate and return a specific array.

+4
source

When you immediately call ToList() you force ReadLines and read the entire file. Continuing, you are now dealing with a List<string> , not an IENumerable .

The reason the second method doesn't work is because you again create two IENumerables that are only (and at the same time, repeatedly) evaluated when you call the Except methods. ToList() for Except simply converts the IENumerable that you get from the Except method to List<string> .

As for why you get an ObjectDisposedException , I think the TextReader will be deleted after the listing, and since you are trying to pass the same IENumeration twice, and ToList() will not help if it is placed at the end of Except s

+1
source

This may require a lot of memory since both files will be downloaded:

 private void GenerateDiff() { var file1Lines = File.ReadLines(firstFile).ToList(); var file2Lines = File.ReadLines(secondFile).ToList(); inFirstNotInSecond = file1Lines.Except(file2Lines); inSecondNotInFirst = file2Lines.Except(file1Lines); } 

The same is true if you are using ReadAllLines .

Less productive solution, but much more efficient in terms of memory:

 void GenerateDiff() { inFirstNotInSecond = File.ReadLines(firstFile).Except(File.ReadLines(secondFile)).ToList(); inSecondNotInFirst = File.ReadLines(secondFile).Except(File.ReadLines(firstFile)).ToList(); } 

Since you are accessing the same files, they are likely to be cached, so the drawback should be minor.

PS: my answer involves delayed execution of Except () .

+1
source

Source: https://habr.com/ru/post/1265704/


All Articles