[c#] C # с использованием ошибки catch


7 Answers

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

Question

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

 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 все еще необходима.




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




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

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, а не просто игнорировать исключение.




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




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

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




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

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

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




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

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

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




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

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




Related