我有一个功能,它使用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...
}
n
和y
组合,从给定文件中跳过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
返回的对象,我相信它不会处理该文件。
我是对的吗?他们不应该列举以避免这种错误吗?如何正确地做到这一点?
答案 0 :(得分:39)
这基本上是File.ReadLines
中的错误,而不是Take
。 ReadLines
会返回一个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
甚至被召唤过。虽然这样做很好,例如DirectoryNotFoundException
和FileNotFoundException
直接被抛出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;
}
}