Ошибка catch оператора c# using

Я просто смотрю на оператор using, я всегда знал, что он делает, но до сих пор не пытался его использовать, я придумал следующий код:

 using (SqlCommand cmd = 
     new SqlCommand(reportDataSource, 
         new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)))
 {
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
     cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
     cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
     cmd.Connection.Open();

     DataSet dset = new DataSet();
     new SqlDataAdapter(cmd).Fill(dset);
     this.gridDataSource.DataSource = dset.Tables[0];
 }

это, кажется, работает, но есть ли в этом какой-то смысл, поскольку, насколько я могу сказать, мне все равно нужно будет заключить это в блок try catch, чтобы поймать непрошеные ошибки, например, sql server. Я что-то упускаю?

насколько я могу видеть в настоящее время, это просто останавливает меня закрытие и удаление cmd, но будет будьте больше строк кода из-за try catch по-прежнему необходимы.

16 ответов


этот код должен быть следующим, чтобы обеспечить своевременное закрытие соединения. Закрытие только команды не закрывает соединение:

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
         {
             cmd.CommandType = CommandType.StoredProcedure;
             cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
             cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
             cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
             cmd.Connection.Open();

             DataSet dset = new DataSet();
             new SqlDataAdapter(cmd).Fill(dset);
             this.gridDataSource.DataSource = dset.Tables[0];
         }

чтобы ответить на ваш вопрос, вы можете сделать то же самое в блоке finally, но это хорошо охватывает код и гарантирует, что вы не забудете очистить.


при выполнении работы ввода-вывода я кодирую ожидал исключение.

SqlConnection conn = null;
SqlCommand cmd = null;

try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;

        conn.Open(); //opens connection

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}
finally
{
    if(conn != null)
        conn.Dispose();

        if(cmd != null)
        cmd.Dispose();
}

Edit: чтобы быть точным, я избегаю используя блок здесь, потому что я считаю, что важно регистрироваться в таких ситуациях. Опыт научил меня, что никогда не знаешь, какое странное исключение может появиться. Вход в эту ситуацию может помочь вам обнаружить взаимоблокировку или найти, где изменение схемы влияет на мало используемую и мало протестированную часть кода база, или любое другое количество проблем.

Edit 2: можно утверждать, что блок using может обернуть try/catch в этой ситуации, и это полностью допустимо и функционально эквивалентно. Это действительно сводится к предпочтению. Вы хотите избежать дополнительных вложений за счет обработки собственного распоряжения? Или вы несете дополнительную вложенность, чтобы иметь автоматическое удаление. Я чувствую, что первое чище, поэтому я делаю это таким образом. Однако я не переписываю последнее, если нахожу его в базе кода, в которой я работаю.

Edit 3: Я очень, очень хотел бы, чтобы MS создала более явную версию using (), которая сделала его более интуитивным, что на самом деле происходит, и дала больше гибкости в этом случае. Рассмотрим следующий воображаемый код:

SqlConnection conn = null;
SqlCommand cmd = null;

using(conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString),
          cmd = new SqlCommand(reportDataSource, conn)
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}

оператор using просто создает try / finally с вызовами Dispose () в finally. Почему бы не дать разработчику унифицированный способ удаления и обработки исключений?


не может быть никакого преимущества в использовании using заявление в этом случае, если вы собираетесь иметь try/catch/ блок. Как вы знаете,using утверждение является синтаксическим сахаром для try/finally что есть


Если ваш код выглядит так:

using (SqlCommand cmd = new SqlCommand(...))
{
  try
  {
    /* call stored procedure */
  }
  catch (SqlException ex)
  {
    /* handles the exception. does not rethrow the exception */
  }
}

тогда я бы рефакторинг это использовать попробовать.. поймать.. наконец-то.

SqlCommand cmd = new SqlCommand(...)
try
{
  /* call stored procedure */
}
catch (SqlException ex)
{
  /* handles the exception and does not ignore it */
}
finally
{
   if (cmd!=null) cmd.Dispose();
}

в этом сценарии я буду обрабатывать исключение, поэтому у меня нет выбора, кроме как добавить эту попытку..поймайте, я мог бы также вставить предложение finally и сохранить себе еще один уровень вложенности. Обратите внимание, что я должен делать что-то в блоке catch, а не просто игнорируя исключения.


подробнее о том, что сказал Крис балланс, в разделе 15.13 спецификации C# (ECMA-334 version 4) говорится: "оператор using переводится на три части: приобретение, использование и утилизация. Использование ресурса неявно заключено в оператор try, который включает предложение finally. Это, наконец, пункт удаляет ресурс. Если получен нулевой ресурс, то вызов Dispose не выполняется и исключение не создается."

описание близко к 2 страницам-стоит читать.

по моему опыту, SqlConnection / SqlCommand может генерировать ошибки так много способов, что вам почти нужно обрабатывать исключения, вызванные больше, чем обрабатывать ожидаемое поведение. Я не уверен, что мне нужно предложение using здесь, так как я хотел бы иметь возможность обрабатывать случай null resource самостоятельно.


использование не связано с ловлей исключений. Речь идет о правильном утилизации ресурсов, которые находятся вне поля зрения сборщика мусора.


одна проблема с "использование" заключается в том, что он не обрабатывает исключения. если дизайнеры "using" добавят "catch" необязательно к его синтаксису, как показано ниже псевдокода, это будет намного полезнее:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...

catch (exception)

   ... handle exception ...

}

it could even have an optional "finally" clause to cleanup anything other than the "MyDisposableObj" allocated at the beginning of the "using" statement... like:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...
   ... open a file or db connection ...

catch (exception)

   ... handle exception ...

finally

   ... close the file or db connection ...

}

по-прежнему не будет необходимости писать код для удаления MyDisposableObj b / c это будет обрабатываться using...

как это сделать?


да, вам все равно нужно будет поймать исключения. Благо с помощью блока вы не добавление к коду. Вы говорите: "в этом блоке кода сделайте что-то, и когда он дойдет до конца, закройте и утилизируйте ресурсы"

Это не совсем необходимо, но он определяет ваши намерения кому-либо еще, используя ваш код, а также помогает не оставлять соединения и т. д. открытыми по ошибке.


здесь есть много отличных ответов, но я не думаю, что это было сказано еще.

несмотря ни на что... метод " Dispose "будет вызываться для объекта в блоке" using". Если вы поместите оператор return или выдадите ошибку, будет вызван "Dispose".

пример:

Я сделал класс под названием "MyDisposable", и он реализует IDisposable и просто делает консоль.Писать. Это всегда пишет в консоль даже во всех этих сценарии:

using (MyDisposable blah = new MyDisposable())
{
    int.Parse("!"); // <- calls "Dispose" after the error.

    return; // <-- calls Dispose before returning.
}

оператор using фактически изменяется в блок try/finally компилятором, в котором параметр блока using удаляется до тех пор, пока он реализует интерфейс IDisposable. Помимо обеспечения правильного расположения указанных объектов, когда они выпадают из области видимости, на самом деле нет захвата ошибок, полученных с помощью этой конструкции.

Как сообщила TheSoftwareJedi выше, вы захотите убедиться, что и SqlConnection и Объекты SqlCommand удаляются правильно. Укладка обоих в один блок использования немного грязная и может не делать то, что вы думаете.

кроме того, помните об использовании блока try/catch в качестве логики. Это запах кода, который мой нос особенно не любит, и часто используется новичками или теми из нас, кто очень спешит уложиться в срок.


FYI, в этом конкретном примере, потому что вы используете ADO.net объект connection and Command, имейте в виду, что оператор using просто выполняет команду.Утилизируйте и соединение.Dispose (), которые фактически не закрывают соединение, а просто освобождают его обратно в ADO.net пул соединений для повторного использования следующим соединением.открыть... что хорошо, и абсолютно правильно делать, bc если вы этого не сделаете, соединение останется непригодным для использования до тех пор, пока сборщик мусора освобождает его обратно в пул, который может быть не до многочисленных других запросов на подключение, которые в противном случае были бы вынуждены создавать новые подключения, даже если есть неиспользуемый, ожидающий сбора мусора.


Я бы принял решение о том, когда и когда не использовать оператор using в зависимости от ресурса, с которым я имею дело. В случае ограниченного ресурса, такого как соединение ODBC, я бы предпочел использовать T/C/F, чтобы я мог регистрировать значимые ошибки в момент их возникновения. Позволить ошибкам драйвера базы данных вернуться к клиенту и потенциально потеряться в обертывании исключений более высокого уровня является неоптимальным.

T/C / F дает вам спокойствие, что ресурс обрабатывается так, как ты хочешь. Как уже упоминалось, оператор using не обеспечивает обработку исключений, он просто гарантирует, что ресурс будет уничтожен. Обработка исключений-это недооцененная и недооцененная языковая структура, которая часто является разницей между успехом и неудачей решения.


Если вызывающий объект вашей функции отвечает за работу с любыми исключениями, оператор using является хорошим способом обеспечения очистки ресурсов независимо от результата.

Это позволяет разместить код обработки исключений на границах слоя / сборки и помогает предотвратить другие функции становятся слишком загромождены.

конечно, это действительно зависит от типов исключений в коде. Иногда вы должны использовать try-catch-finally вместо использования заявление. Моя привычка-всегда начинать с оператора using для IDisposables (или иметь классы, содержащие IDisposables, также реализующие интерфейс) и добавлять try-catch-finally по мере необходимости.


Итак, в основном, " использование "точно такое же, как" Try/catch/finally", только гораздо более гибкое для обработки ошибок.


незначительная поправка к примеру:SqlDataAdapter также необходимо создать экземпляр в using о себе:

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    con.Open();

    DataSet dset = new DataSet();
    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
    {
        adapter.Fill(dset);
    }
    this.gridDataSource.DataSource = dset.Tables[0];
}

во-первых, ваш пример кода должен быть:

using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}

С кодом в вашем вопросе исключение, создающее команду, приведет к тому, что только что созданное соединение не будет удалено. С вышеуказанным, соединение правильно размещано.

Если вам нужно обрабатывать исключения в строительство соединения и команды (а также при их использовании), да, вы должны обернуть все это в try / catch:

try
{
    using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
    using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
        cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
        cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Connection.Open();

        DataSet dset = new DataSet();
        new SqlDataAdapter(cmd).Fill(dset);
        this.gridDataSource.DataSource = dset.Tables[0];
    }
}
catch (RelevantException ex)
{
    // ...handling...
}

но вы не нужно обрабатывать очистку conn или cmd, это уже сделали для вас.

контраст с тем же самым без using:

SqlConnection conn = null;
SqlCommand cmd = null;
try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch (RelevantException ex)
{
    // ...handling...
}
finally
{
    if (cmd != null)
    {
        try
        {
            cmd.Dispose();
        }
        catch { }
        cmd = null;
    }
    if (conn != null)
    {
        try
        {
            conn.Dispose();
        }
        catch { }
        conn = null;
    }
}
// And note that `cmd` and `conn` are still in scope here, even though they're useless

Я знаю, что я лучше пишу. :-)