Использование с помощью утилизации вложенных объектов - PullRequest
5 голосов
/ 20 апреля 2010

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

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

Ответы [ 4 ]

8 голосов
/ 20 апреля 2010

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

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

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

Редактировать: Рассмотрим этот пример , а не , используя внутренний оператор 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())
{
}

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

4 голосов
/ 21 апреля 2010

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

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

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

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

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

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

4 голосов
/ 20 апреля 2010

Вы должны вызывать 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 или для оператора) и с помощью использованияэто целый блок, вам не нужны скобки, и это выглядит аккуратнее, на мой взгляд.Некоторые скажут, что вы всегда должны использовать фигурные скобки, потому что слишком легко случайно добавить второе утверждение и создать ошибку, но в этом случае я думаю, что это маловероятно.

0 голосов
/ 20 апреля 2010

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

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

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

...