С# с использованием ошибки catch

Я просто смотрю на инструкцию 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];
 }

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

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

Ответ 1

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

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, но это хорошо отображает код и гарантирует, что вы не забыли очистить.

Ответ 2

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

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

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

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

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

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. Почему бы не дать разработчику единый способ утилизации и обработки исключений?

Ответ 3

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

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

Но, если шаблонный оператор using будет скрываться, есть все равно, продолжайте и обрабатывайте вещи сами, если вы предпочитаете.

Ответ 4

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

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

Тогда я бы реорганизовал его, чтобы использовать try.. catch.. наконец, вместо этого.

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

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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

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...

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

Ответ 8

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

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

Ответ 9

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

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

Пример:

Я создал класс под названием "MyDisposable", и он реализует IDisposable и просто создает Console.Write. Он всегда записывает на консоль даже во всех этих сценариях:

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

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

Ответ 10

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

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

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

Ответ 11

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

Ответ 12

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

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

Ответ 13

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

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

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

Ответ 14

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

Ответ 15

Незначительная коррекция к примеру: 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];
}

Ответ 16

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

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

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