Использование использования для удаления вложенных объектов

Если у меня есть код с такими вложенными объектами, как этот, мне нужно использовать вложенные операторы using, чтобы убедиться, что объекты SQLCommand и SQLConnection утилизированы должным образом, как показано ниже, или я в порядке, если код, создающий экземпляр SQLCommand, находится в пределах оператор внешнего использования.

using (var conn = new SqlConnection(sqlConnString))
{
    using (var cmd = new SqlCommand())
    {
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = cmdTextHere;
        conn.Open();

        cmd.Connection = conn;
        rowsAffected = cmd.ExecuteNonQuery();
    }
}

person etoisarobot    schedule 20.04.2010    source источник


Ответы (4)


да. Вы могли бы немного очистить это

using (SqlConnection conn = new SqlConnection(sqlConnString))
using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand())
{
   // code here
}

Но вам все равно понадобится using для каждого объекта IDisposable.

Изменить: рассмотрим этот пример not с использованием внутреннего оператора using.

class A : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("A Disposed");
    }
}

class B : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("B Disposed");
    }
}

Код

using (A a = new A())            
{
    B b = new B();
}

В конце блока правильно расположена буква A. Что происходит с B? Что ж, это выходит за рамки и просто ждет сборки мусора. B.Dispose () не вызывается. С другой стороны

using (A a = new A())
using (B b = new B())
{
}

Когда выполнение покидает блок (фактически, блоки), скомпилированный код выполняет вызовы метода Dispose () каждого объекта.

person Anthony Pegram    schedule 20.04.2010
comment
Есть какая-то конкретная причина, по которой это всегда должно быть явным? Этот ответ отклоняется от истинной цели вопроса. - person Nayan; 20.04.2010
comment
@Nayan, смотри мой комментарий к твоему ответу. - person Anthony Pegram; 20.04.2010
comment
Предупреждение: не делайте этого для классов Stream, поскольку они владеют переданными потоками! См. stackoverflow.com/questions/1065168/ < / а> - person fmuecke; 08.03.2012

Вы должны вызвать dispose для обоих, однако его будет легче читать, если вы просто сделаете это:

using (SqlConnection conn = new SqlConnection(sqlConnString)) 
using (SqlCommand cmd = new SqlCommand()) 
{ 
     cmd.CommandType = CommandType.Text; 
     cmd.CommandText = cmdTextHere; 
     conn.Open(); 

     cmd.Connection = conn; 
     rowsAffected = cmd.ExecuteNonQuery(); 
} 

поскольку создается неявный блок (например, оператор if или оператор for) и поскольку using представляет собой весь блок, фигурные скобки не нужны, и, на мой взгляд, он выглядит аккуратнее. Некоторые скажут, что вы всегда должны использовать фигурные скобки, потому что слишком легко случайно добавить второй оператор и создать ошибку, но в данном случае я думаю, что это маловероятно.

person Erik Funkenbusch    schedule 20.04.2010
comment
Аккуратно, да. Но это все еще не дает ответа на вопрос. : / - person Nayan; 20.04.2010
comment
Да. Вы должны вызвать dispose для обоих, таким образом, оператор using для обоих. - person Erik Funkenbusch; 20.04.2010

Вы можете не использовать символы вокруг SqlCommand. ГХ в конечном итоге очистит это за вас. Однако я настоятельно не рекомендую этого делать. Я объясню почему.

SqlCommand косвенно наследуется от System.ComponentModel.Component и, следовательно, наследует его Finalizer метод. Отсутствие вызова dispose для SqlCommand гарантирует, что команда будет продвинута по крайней мере на одно поколение после того, как она выйдет из области видимости (сборщик мусора .NET - это generational gc). Например: когда команда была в первом поколении, она перейдет в поколение 2. Финализируемые объекты дольше хранятся в памяти, чтобы финализатор мог безопасно работать. Но не только сама команда хранится в памяти, но и все объекты, на которые она ссылается, передаются этому поколению. Объекты, на которые он будет ссылаться, - это SqlConnection, список SqlParameter объектов, возможно большая строка CommandText и многие другие внутренние объекты, на которые он ссылается. Эта память может быть удалена только при сборе этого поколения, но чем выше поколение, тем реже оно очищается.

Таким образом, отказ от вызова вызовет дополнительную нагрузку на память и дополнительную работу для потока финализатора.

Когда .NET не может выделить новую память, среда CLR принудительно выполняет сборку мусора всех поколений. После этого в среде выполнения обычно снова будет достаточно места для размещения новых объектов. Однако, когда этот принудительный сбор происходит, когда в памяти есть много объектов, которые все еще необходимо повысить до следующего поколения (поскольку они финализируемы или на них ссылается финализируемый объект), возможно, что CLR не может освободить достаточно памяти. Результатом этого будет OutOfMemoryException.

Должен признаться, я никогда не видел, чтобы это происходило, потому что разработчики распоряжались не только своими SqlCommand объектами. Однако я часто видел OOM в производственных системах, вызванных неправильным удалением объектов.

Я надеюсь, что это дает немного информации о том, как работает сборщик мусора и каков риск неправильной утилизации (финализируемого) объекта. Я всегда выбрасываю все одноразовые предметы. Хотя взгляд в Reflector может доказать, что это не обязательно для определенного типа, такой тип программирования приводит к тому, что код менее удобен в обслуживании и заставляет код зависеть от внутреннего поведения типа (и это поведение может измениться в будущем).

person Steven    schedule 20.04.2010
comment
Спасибо за объяснение, Стивен! - person Nayan; 21.04.2010

Чтобы ответить на ваш вопрос, да, вы можете это сделать.

Поскольку объект SqlCommand ограничен внешними скобками, он будет собран сборщиком мусора, когда выполнение выйдет за пределы блока.

Другие ответы тоже допустимы, но они не дали точного ответа на ваш вопрос :)

person Nayan    schedule 20.04.2010
comment
Сборка мусора - это не то же самое, что вызов .Dispose (). Кроме того, GC является неопределенным, вы не знаете точно, когда объект будет собран. - person Anthony Pegram; 20.04.2010
comment
Использование сборщика мусора для сбора экземпляра SqlCommand может вызвать очень неприятные утечки памяти, в результате чего многие экземпляры останутся в памяти. - person Marek; 21.04.2010
comment
Я согласен с Мареком. Прочтите, пожалуйста, мой ответ. Это длинная версия ответа Марека :-). - person Steven; 21.04.2010