Datareader를 반복하기 위해 Yield를 사용하면 연결이 닫히지 않을 수 있습니까?

StackOverflow https://stackoverflow.com/questions/47521

  •  09-06-2019
  •  | 
  •  

문제

다음은 인터넷 검색 중에 몇 군데에서 찾은 항복 키워드를 사용하여 데이터베이스에서 데이터를 검색하는 샘플 코드입니다.

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;
}

치명적이지 않을 수도 있는 DB 연결의 경우 GC가 결국 이를 정리할 것이라고 가정합니다. 하지만 연결 대신 더 중요한 리소스라면 어떨까요?

도움이 되었습니까?

해결책

컴파일러가 합성하는 Iterator는 foreach 루프가 종료될 때 foreach 호출을 수행하는 IDisposable을 구현합니다.

Iterator의 Dispose() 메서드는 초기 종료 시 using 문을 정리합니다.

foreach 루프, using() 블록에서 반복기를 사용하거나 다른 방식으로 Dispose() 메서드를 호출하는 한 Iterator 정리가 수행됩니다.

다른 팁

"using" 블록 내에서 연결을 사용하고 있으므로 연결이 자동으로 닫힙니다.

내가 시도한 간단한 테스트에 따르면 aku가 맞습니다. foreach 블록이 종료되자마자 dispose가 호출됩니다.

@데이비드:그러나 호출 스택은 호출 사이에 유지되므로 다음 호출에서는 while 블록인 항복 이후의 다음 명령으로 돌아가기 때문에 연결이 닫히지 않습니다.

내 이해는 반복자가 삭제되면 연결도 함께 삭제된다는 것입니다.또한 Connection.Close는 using 절로 인해 개체가 삭제될 때 처리되므로 필요하지 않을 것이라고 생각합니다.

다음은 동작을 테스트하려고 시도한 간단한 프로그램입니다.

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;
    }
}

으로 판단하면 이 기술적 설명, 첫 번째 항목을 반환할 때 연결이 이미 닫혔기 때문에 코드가 예상대로 작동하지 않지만 두 번째 항목에서 중단됩니다.

@조엘 고브로:예, 계속 읽어야 했습니다. 3부 이 시리즈에서는 컴파일러가 finally 블록에 대한 특수 처리를 추가하여 다음 위치에서만 트리거한다고 설명합니다. 진짜 끝.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top