C # с помощью оператора ловить ошибку - PullRequest
28 голосов
/ 30 октября 2008

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

 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 вниз. Я что-то упустил?

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

Ответы [ 16 ]

57 голосов
/ 30 октября 2008

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

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: Можно утверждать, что блок использования может обернуть попытку / поймать в этой ситуации, и это полностью допустимо и функционально эквивалентно. Это действительно сводится к предпочтениям. Вы хотите избежать лишних вложений за счет обработки вашего собственного захоронения? Или вы берете на себя дополнительное вложение, чтобы иметь автоматическое удаление. Я чувствую, что первое чище, поэтому я делаю это таким образом. Однако я не переписываю последнее, если найду его в кодовой базе, в которой я работаю.

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

18 голосов
/ 30 октября 2008

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

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

14 голосов
/ 30 октября 2008

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

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

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

6 голосов
/ 30 октября 2008

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

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

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

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 голосов
/ 30 октября 2008

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

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

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

4 голосов
/ 22 июня 2011

Одна проблема с использованием - это то, что он не обрабатывает исключения. если бы разработчики «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 ...

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

4 голосов
/ 30 октября 2008

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

2 голосов
/ 30 октября 2008

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

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

Пример:

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

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

    return; // <-- calls Dispose before returning.
}
2 голосов
/ 30 октября 2008

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

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

1 голос
/ 29 июня 2011

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

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

...