15

これは、グーグル検索中にいくつかの場所で見つけたyieldキーワードを使用してデータベースからデータを取得するサンプルコードです。

public IEnumerable<object> ExecuteSelect(string commandText)
{
    using (IDbConnection connection = CreateConnection())
    {
        using (IDbCommand cmd = CreateCommand(commandText, connection))
        {
             connection.Open();
             using (IDbDataReader reader = cmd.ExecuteReader())
             {
                while(reader.Read())
                {
                    yield return reader["SomeField"];
                }
             }
             connection.Close();
        }
    }
}

このサンプル コードでは、 datareader 全体を繰り返し処理しないと接続が閉じられないと考えてよろしいですか?

収量を正しく理解していれば、接続を閉じない例を次に示します。

foreach(object obj in ExecuteSelect(commandText))
{
  break;
}

壊滅的ではない可能性のあるデータベース接続の場合、GC は最終的にそれをクリーンアップすると思いますが、接続ではなく、より重要なリソースである場合はどうなるでしょうか?

4

4 に答える 4

12

コンパイラが合成する Iterator は、ループが終了したときIDisposableforeach呼び出されるを実装します。foreach

Iterator のメソッドは、早期終了時にステートメントDispose()をクリーンアップします。using

foreachイテレータをループ、using()ブロック、またはDispose()他の方法でメソッドを呼び出している限り、イテレータのクリーンアップが行われます。

于 2008-09-06T20:15:18.453 に答える
2

私が試した簡単なテストから、akuは正しいです。foreachブロックが終了するとすぐにdisposeが呼び出されます。

@David:ただし、呼び出しスタックは呼び出し間で保持されるため、次の呼び出しで、whileブロックであるyieldの後に次の命令に戻るため、接続は閉じられません。

私の理解では、イテレータを破棄すると、接続もイテレータとともに破棄されます。また、using句によりオブジェクトが破棄されるときに処理されるため、Connection.Closeは必要ないと思います。

これが私が動作をテストしようとした簡単なプログラムです...

class Program
{
    static void Main(string[] args)
    {
        foreach (int v in getValues())
        {
            Console.WriteLine(v);
        }
        Console.ReadKey();

        foreach (int v in getValues())
        {
            Console.WriteLine(v);
            break;
        }
        Console.ReadKey();
    }

    public static IEnumerable<int> getValues()
    {
        using (TestDisposable t = new TestDisposable())
        {
            for(int i = 0; i<10; i++)
                yield return t.GetValue();
        }
    }
}

public class TestDisposable : IDisposable
{
    private int value;

    public void Dispose()
    {
        Console.WriteLine("Disposed");
    }

    public int GetValue()
    {
        value += 1;
        return value;
    }
}
于 2008-09-06T19:10:11.743 に答える
2

「using」ブロック内で使用しているため、接続は自動的に閉じられます。

于 2008-09-06T15:01:35.523 に答える
0

この技術的な説明から判断すると、最初のアイテムを返すときに接続が既に閉じられているため、コードは期待どおりに動作しませんが、2 番目のアイテムで中止されます。

@Joel Gauvreau : はい、読み進める必要がありました。このシリーズの第 3 回では、コンパイラーが、finally ブロックに特別な処理を追加して、実際の最後でのみトリガーすることについて説明します。

于 2008-09-06T18:10:16.660 に答える