Использование выхода для перебора устройства чтения данных может не закрыть соединение?

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

  •  09-06-2019
  •  | 
  •  

Вопрос

Вот пример кода для извлечения данных из базы данных с использованием ключевого слова доходности, которое я нашел в нескольких местах при поиске в Google:

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

Прав ли я, полагая, что в этом примере кода соединение не будет закрыто, если мы не обработаем весь модуль чтения данных?

Вот пример, который не закроет соединение, если я правильно понимаю доходность.

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

Я полагаю, что для соединения с БД, которое не может быть катастрофическим, GC в конечном итоге очистит его, но что, если вместо соединения это будет более важный ресурс?

Это было полезно?

Решение

Итератор, синтезируемый компилятором, реализует IDisposable, который foreach вызывает при выходе из цикла foreach.

Метод Dispose() итератора очистит операторы using при раннем выходе.

Пока вы используете итератор в цикле foreach, блоке using() или вызываете метод Dispose() каким-либо другим способом, очистка итератора будет происходить.

Другие советы

Соединение будет закрыто автоматически, поскольку вы используете его внутри блока «using».

Судя по простому тесту, который я попробовал, Аку прав: Dispose вызывается сразу после выхода из блока foreach.

@Дэйвид :Однако стек вызовов сохраняется между вызовами, поэтому соединение не будет закрыто, поскольку при следующем вызове мы вернемся к следующей инструкции после выхода, то есть к блоку 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