Возврат дохода внутри
Если я правильно помню, что, когда я использовал выход внутри блоков using SqlConnection
, я получил исключения во время выполнения.
using (var connection = new SqlConnection(connectionString))
{
var command = new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
yield reader[0];
}
// Call Close when done reading.
reader.Close();
}
Эти проблемы были решены, когда я заменил yield
на List, где я добавил элементы каждой итерации.
Та же проблема еще не дошла до меня, когда внутри using StreamReader
блоков
using (var streamReader = new StreamReader(fileName))
{
string line;
while ((line = streamReader.ReadLine()) != null)
{
yield return line;
}
}
Есть ли объяснение, почему Исключения произошли в первом случае, а не в последнем? Является ли эта конструкция целесообразной?
EDIT Чтобы получить ошибку (раннее удаление), которую я сделал в прошлом, вы должны вызвать первый метод ниже:
IEnumerable<string> Read(string fileName)
{
using (var streamReader = new StreamReader(fileName))
{
return Read(streamReader);
} // Dispose will be executed before ReadLine() because of deffered execution
}
IEnumerable<string> Read(StreamReader streamReader)
{
string line;
while ((line = streamReader.ReadLine()) != null)
{
yield return line;
}
}
То же самое можно сделать с другими способами отсрочки выполнения, например System.Linq.Enumerable.Select()
Ответы
Ответ 1
См. этот пост для хорошего объяснения проблем с using
и yield
. Поскольку вы вернетесь в перечислителе, блок использования уже уничтожит контекст, прежде чем что-либо будет доступно. Ответы имеют хорошие решения, в основном, либо делают метод обертки перечислителем, либо строят список.
Также обычно более удобно иметь using
вокруг читателя, а не соединение, и использовать CommandBehavior.CloseConnection
для обеспечения освобождения ресурсов при чтении. Хотя в вашей ситуации это не имеет особого значения, если вы когда-нибудь вернете считыватель данных из метода, это обеспечит правильное закрытие соединения, когда устройство будет установлено.
using(SqlDataReader reader =
command.ExecuteReader(CommandBehavior.CloseConnection)) {
while (reader.Read())
{
yield reader[0];
}
}
Ответ 2
Компилятор должен обрабатывать yield
внутри блока using
правильно в обоих случаях. Нет очевидной причины, по которой он должен вызывать исключение.
Одна вещь, о которой нужно знать, заключается в том, что соединение будет удалено только после того, как вы завершили итерацию и/или вручную разместили объект перечислителя. Если вы обнародуете этот код в общедоступном методе, то возможно, что этот глупый или вредоносный код может долго держать ваше соединение открытым:
var enumerable = YourMethodThatYieldsFromTheDataReader();
var enumerator = enumerable.GetEnumerator();
enumerator.MoveNext();
Thread.Sleep(forever); // your connection will never be disposed