Ответ 1
Вы обнаружили ошибку в структуре, насколько я могу судить. Это разумно тонко, из-за взаимодействия нескольких вещей:
- Когда вы вызываете
ReadLines()
, файл фактически открывается. Лично я думаю об этом как о самой ошибке; Я ожидал бы и надеюсь, что это будет лениво - только открытие файла, когда вы пытаетесь начать повторять его. - Когда вы вызываете
GetEnumerator()
первый раз при возврате значенияReadLines
, он фактически вернет ту же ссылку. - Когда
First()
вызываетGetEnumerator()
, он создаст клон. Это будет иметь тот жеStreamReader
, что иtextEnumerator
- Когда
First()
удаляет свой клон, он утилизируетStreamReader
и устанавливает свою переменную вnull
. Это не влияет на переменную в оригинале, которая теперь относится к расположенномуStreamReader
- Когда
Last()
вызываетGetEnumerator()
, он создаст клон исходного объекта, в комплекте с которыми находитсяStreamReader
. Затем он пытается прочитать этот читатель и выдает исключение.
Теперь сравните это со своей второй версией:
- Когда
First()
вызываетGetEnumerator()
, возвращается исходная ссылка с открытым считывателем. - Когда
First()
затем вызываетDispose()
, читатель будет удален, а переменная установлена наnull
- Когда
Last()
вызываетGetEnumerator()
, будет создан клон, но поскольку значение, которое оно клонирует, имеет ссылкуnull
, создается новыйStreamReader
, поэтому он может без проблем читать файл. Затем он располагает клоном, который закрывает читателя. - Когда вызывается
GetEnumerator()
, второй клон исходного объекта, открывающий еще одинStreamReader
- опять же, проблем нет.
Таким образом, проблема в первом фрагменте заключается в том, что вы вызываете GetEnumerator()
второй раз (в First()
), не удаляя первый объект.
Вот еще один пример той же проблемы:
using System;
using System.IO;
using System.Linq;
class Test
{
static void Main()
{
var lines = File.ReadLines("test.txt");
var query = from x in lines
from y in lines
select x + "/" + y;
foreach (var line in query)
{
Console.WriteLine(line);
}
}
}
Вы можете исправить это, вызвав File.ReadLines
дважды - или используя истинно ленивую реализацию ReadLines
, например:
using System.IO;
using System.Linq;
class Test
{
static void Main()
{
var lines = ReadLines("test.txt");
var query = from x in lines
from y in lines
select x + "/" + y;
foreach (var line in query)
{
Console.WriteLine(line);
}
}
static IEnumerable<string> ReadLines(string file)
{
using (var reader = File.OpenText(file))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
}
В последнем коде новый StreamReader
открывается каждый раз, когда вызывается GetEnumerator()
, поэтому результатом является каждая пара строк в test.txt.