Datareader를 반복하기 위해 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;
}
치명적이지 않을 수도 있는 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;
}
}