简体   繁体   English

IEnumerable的 <string> System.ObjectDisposedException

[英]IEnumerable<string> System.ObjectDisposedException

On some machines but not on others I get System.ObjectDisposedException using this class. 在某些机器上但在其他机器上没有,我使用此类获得System.ObjectDisposedException

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: GenerateDiff()修改为:

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. 我在这里使用这个类diff的实例。 No using or Dispose anywhere. 不得在任何地方usingDispose 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.) (我们的猜测是,这是因为IEnumerable<>实现了延迟加载,垃圾收集器在我想访问InFirstNotInSecondInSecondNotInFirst之前关闭了阅读器。但是使用GC.Collect()在某些机器上仍然没有异常。)

Using the source code we see that File.ReadLines returns a ReadLinesIterator . 使用源代码,我们看到File.ReadLines返回一个ReadLinesIterator

And here you can see they Dispose after enumeration. 在这里你可以看到他们在枚举后Dispose。

That means that the enumeration with File.ReadLines can happen only once. 这意味着File.ReadLines的枚举只能发生一次。 It's better to use File.ReadAllLines which will enumerate first and return a concrete array. 最好使用File.ReadAllLines ,它将首先枚举并返回一个具体的数组。

With the immediate call to ToList() you force ReadLines to execute immediately and read the entire file. 通过立即调用ToList()您可以强制ReadLines立即执行并读取整个文件。 Going on, you now are dealing with a List<string> and not an IENumerable anymore. 继续,你现在正在处理List<string>而不是IENumerable

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. 第二种方法不起作用的原因是,您再次创建了两个仅在(并且同时重复)在调用Except方法时进行求值的IENumerables The ToList() behind the Except just converts the IENumerable you get from the Except method to a List<string> . Except后面的ToList()只是将您从Except方法获得的IENumerable转换为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 至于为什么你得到一个ObjectDisposedException我猜想TextReader将在枚举之后被处理掉,因为你试图通过相同的IENumeration两次ToList()如果把它放在Except的末尾就无济于事了。

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 . 如果您使用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. 由于您正在访问相同的文件,因此它们可能会被缓存,因此缺点应该可以忽略不计。

PS: my answer is assuming deferred execution of Except() . PS:我的答案是假设延迟执行 Except()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM