私が知る限り、あなたはフレームワークにバグを発見しました。いくつかのことが相互作用するため、かなり微妙です。
- を呼び出す
ReadLines()
と、ファイルが実際に開かれます。個人的には、これ自体がバグだと考えています。私はそれが怠惰であることを期待し、望んでいます-反復処理を開始しようとしたときにのみファイルを開きます。
- の戻り値で初めて呼び出すと
GetEnumerator()
、実際には同じ参照が返されます。ReadLines
- を
First()
呼び出すとGetEnumerator()
、クローンが作成されます。StreamReader
これは同じものを共有しますtextEnumerator
- が
First()
クローンを破棄すると、 が破棄され、変数が にStreamReader
設定されます。これは元の変数には影響しません。現在は破棄された変数を参照しています。null
StreamReader
- を
Last()
呼び出すと、元のオブジェクトGetEnumerator()
のクローンが作成され、破棄が完了します。次に、そのリーダーから読み取ろうとし、例外をスローします。StreamReader
これを 2 番目のバージョンと比較します。
- を
First()
呼び出すGetEnumerator()
と、元の参照が返され、リーダーが開かれます。
- その後
First()
が呼び出さDispose()
れると、リーダーが破棄され、変数がに設定されますnull
- を
Last()
呼び出すGetEnumerator()
と、クローンが作成されますが、クローンの値にはnull
参照があるため、新しいStreamReader
ものが作成されるため、問題なくファイルを読み取ることができます。次に、クローンを破棄し、リーダーを閉じます
- が
GetEnumerator()
呼び出されると、元のオブジェクトの 2 番目のクローンが作成され、さらに別のオブジェクトが開かれますStreamReader
。これも問題ありません。
基本的に、最初のスニペットの問題は、最初のオブジェクトを破棄せずにGetEnumerator()
( で) 2 回目の呼び出しを行っていることです。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);
}
}
}
これを修正するには、 を 2 回呼び出すか、次のよう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 の各行のペアになります。