On some machines but not on others I get 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 modifying 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 can't reproduce the exception.
Interesting thing is that this is not working:
private void GenerateDiff()
{
var file1Lines = File.ReadLines(firstFile);
var file2Lines = File.ReadLines(secondFile);
inFirstNotInSecond = file1Lines.Except(file2Lines).ToList();
inSecondNotInFirst = file2Lines.Except(file1Lines).ToList();
}
I'm using an instance of this class diff
here for example. No using
or Dispose
anywhere. No tasks or threads.
if (diff.InSecondNotInFirst.Any(s => !s.Contains("bxsr")))
Could someone please explain the root cause? Thank you.
(Our guess is that this is because of IEnumerable<>
implements lazy loading and the garbage collector closes the reader before I want to access InFirstNotInSecond
or InSecondNotInFirst
. But using GC.Collect()
there are still no exception on some machines.)
Using the source code we see that File.ReadLines
returns a ReadLinesIterator
.
And here you can see they Dispose after enumeration.
That means that the enumeration with File.ReadLines
can happen only once.
It's better to use File.ReadAllLines
which will enumerate first and return a concrete array.
With the immediate call to ToList()
you force ReadLines
to execute immediately and read the entire file. Going on, you now are dealing with a List<string>
and not an IENumerable
anymore.
The reason the second method doesn't work is, that you are, again, creating two IENumerables
that are only (and at the same time repeatedly) evaluated when the Except
methods are called. The ToList()
behind the Except
just converts the IENumerable
you get from the Except
method to a List<string>
.
As to why you get a ObjectDisposedException
I guess that the TextReader
will be disposed after being enumerated and since you are trying to go through the same IENumeration
s twice a ToList()
won't help if it's placed at the end of the Except
s
This may require a lot of memory as both files will be loaded:
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 truth if you use ReadAllLines
.
A little less performant solution, but much more memory efficient:
void GenerateDiff()
{
inFirstNotInSecond = File.ReadLines(firstFile).Except(File.ReadLines(secondFile)).ToList();
inSecondNotInFirst = File.ReadLines(secondFile).Except(File.ReadLines(firstFile)).ToList();
}
Since you are accessing same files they are likely to be cached, so drawback should be negligible.
P.S.: my answer is assuming deferred execution of Except().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With