File_adLines上的IEnumerable.Take(0)似乎没有处理/关闭File句柄

时间:2016-08-25 10:19:26

标签: c#

我有一个功能,它使用sub connectSSH{ my $user = "..."; my $password = "..."; my $ssh2 = Net::SSH2->new(); my $chan; if($ssh2->connect($ip,$port,Timeout => 10)){ if(!$ssh2->auth_password($user, $password)){ print"Error: Password wrong\n"; exit; }else{ $chan = $ssh2->channel(); # SSH $chan->blocking(0); $chan->shell(); } }else{ print "Connection to $ip not possible\n"; exit; } return $chan; } sub sendCommand{ my ($chan,$command) = @_; my @output=(); print $chan "$command\n"; #usleep(500) push(@output,"$_") while <$chan>; #process output... } ny组合,从给定文件中跳过File.ReadLines行代码和Skip行。当我下次尝试打开Take给出的文件时:

filePath

我在&#34; string[] Lines = File.ReadLines(filePath).Skip(0).Take(0).ToArray(); using (StreamWriter streamWriter = new StreamWriter(filePath)) { // ... } &#34;上获得File in use by another process例外线。

看起来using是罪魁祸首,因为它返回一个空的IEnumerable.Take(0)而没有枚举IEnumerable返回的对象,我相信它不会处理该文件。

我是对的吗?他们不应该列举以避免这种错误吗?如何正确地做到这一点?

3 个答案:

答案 0 :(得分:39)

这基本上是File.ReadLines中的错误,而不是TakeReadLines会返回一个IEnumerable<T>,它在逻辑上应该是懒惰的,但急切地会打开该文件。除非您实际迭代返回值,否则无需处置。

它的在仅迭代一次方面被打破了。例如,您应该能够写:

var lines = File.ReadLines("text.txt");
var query = from line1 in lines
            from line2 in lines
            select line1 + line2;

...应该给出文件中的行的交叉产品。由于破碎,它没有。

File.ReadLines 应该实现如下:

public static IEnumerable<string> ReadLines(string filename)
{
    return ReadLines(() => File.OpenText(filename));
}

private static IEnumerable<string> ReadLines(Func<TextReader> readerProvider)
{
    using (var reader = readerProvider())
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

不幸的是,它不是:(

选项:

  • 使用上述代替File.ReadLines
  • 编写自己的Take实现,其中总是开始迭代,例如

    public static IEnumerable<T> Take<T>(this IEnumerable<T> source, int count)
    {
        // TODO: Argument validation
        using (var iterator = source.GetEnumerator())
        {
            while (count > 0 && iterator.MoveNext())
            {
                count--;
                yield return iterator.Current;
            }
        }
    }
    

答案 1 :(得分:18)

从上面File.ReadLines() in the Reference Source的评论中,很明显负责的团队知道关于这个&#34; bug&#34;:

  

无法更改以保持与4.0兼容的已知问题:

     
      
  • 之前为StreamReader预先分配了基础IEnumerable<T>   GetEnumerator甚至被召唤过。虽然这样做很好,例如   DirectoryNotFoundExceptionFileNotFoundException直接被抛出   File.ReadLines(用户可能期望),它也意味着读者   如果用户实际上没有对可枚举的事物进行预告,那么将被泄露(因此   调用处理至少一个IEnumerator<T>实例)
  •   

所以他们希望File.ReadLines()在传递无效或不可读路径时立即抛出,而不是在枚举时抛出。

替代方案很简单:如果您对其内容实际上并不感兴趣,请不要调用Take(0),或者不要完全阅读该文件。

答案 2 :(得分:-1)

在我看来,根本原因是Enumerable.Take如果count为零,迭代器不会处置底层迭代器,因为代码没有进入foreach循环 - 请参阅referencesource。 如果以下列方式修改代码,问题就会得到解决:

static IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count)
{
    foreach (TSource element in source)
    {
        if (--count < 0) break;
        yield return element;
    }
}