Я просто смотрю на оператор 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 сервер не работает. Я что-то упускаю?
Насколько я сейчас вижу, это просто мешает мне закрывать и удалять cmd, но будет больше строк кода из-за того, что try catch все еще необходим.





Использование не связано с перехватом исключений. Речь идет о правильном удалении ресурсов, которые находятся вне поля зрения сборщика мусора.
Ресурсы не должны находиться вне поля зрения сборщика мусора. По-прежнему полезно очистить их как можно скорее, чем ждать сборки мусора.
это тоже не о сборщике мусора.
Да, вам все равно нужно ловить исключения. Преимущество блока 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;
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();
}
Редактировать: Чтобы быть точным, я избегаю блока с использованием здесь, потому что считаю важным входить в такие ситуации. Опыт научил меня, что никогда не знаешь, какое странное исключение может появиться. Ведение журнала в этой ситуации может помочь вам обнаружить тупиковую ситуацию или найти, где изменение схемы влияет на малоиспользуемую и мало протестированную часть вашей кодовой базы, или на любое количество других проблем.
Изменить 2: Можно утверждать, что в этой ситуации блок using может обернуть команду try / catch, и это полностью верно и функционально эквивалентно. Это действительно зависит от предпочтений. Вы хотите избежать дополнительных вложений за счет собственной утилизации? Или вы берете на себя дополнительное вложение, чтобы иметь автоматическое удаление. Я чувствую, что первое чище, поэтому делаю так. Однако я не переписываю последнее, если найду его в кодовой базе, в которой я работаю.
Изменить 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 делает то же самое гораздо более элегантно?
Итак, я могу зарегистрировать исключение. Нет ничего элегантного в исключении времени выполнения на рабочем столе пользователя в удаленной части страны / мира, и вы понятия не имеете, что пошло не так.
Но явное удаление или использование 'using' абсолютно не влияет на то, как вы регистрируете исключения. Вы можете легко использовать блок using, а внутри блока using иметь блок try, в котором выполняется операция, и блок catch для регистрации исключений.
Я просто осознаю недостаток использования using, когда пытаюсь разобраться с Exception. Я согласен с @Jason Jackson. using транслируется в try/finally во время компиляции. Когда я хочу перехватить Exception, добавление try/catch к using просто создаст ДВА пункта try, которые повлияют на производительность. Еще хуже, когда .Dispose() обычно не закрывает соединение до '.Close ()' или ожидает сборки мусора. Тогда почему бы просто не написать «попробуй / поймай / наконец»! Я проверю на ноль, соединение открыто для закрытия и помещаю его в блок final. Так правильнее и лучше.
@CallMeLaNN - ДА! Меня это беспокоило долгое время. Я получаю языковые ярлыки для определенных типов, таких как using (), привязанные к IDisposable, но вместо этого я хотел бы, чтобы они включили его во что-то более явное. См. Мой новый пример выше.
просто интересно, почему вы явно инициализируете их нулевыми значениями в своем первом блоке кода? почему бы просто не сделать это в блоке try? SqlConnection conn = null; SqlCommand cmd = null;
Оператор using фактически превращается компилятором в блок try / finally, в котором удаляется параметр блока using, если он реализует интерфейс IDisposable. Помимо обеспечения правильного размещения указанных объектов, когда они выпадают из области видимости, на самом деле нет захвата ошибок, получаемых при использовании этой конструкции.
Как упоминалось выше в Программное обеспечениеДжедаи, вам нужно убедиться, что объекты SqlConnection и SqlCommand удаляются должным образом. Объединение обоих в один блок using немного беспорядочно и может не делать того, что вы думаете.
Также помните об использовании блока try / catch в качестве логики. Это запах кода, который не нравится моему носу, и который часто используется новичками или теми из нас, кто очень спешит уложиться в срок.
К вашему сведению, в этом конкретном примере, поскольку вы используете соединение ADO.net и объект Command, имейте в виду, что оператор using просто выполняет Command.Dispose и Connection.Dispose (), которые на самом деле не закрывают соединение, но просто освобождает его обратно в пул подключений ADO.net для повторного использования при следующем подключении. open ... что хорошо, и это абсолютно правильно, bc, если вы этого не сделаете, подключение останется неиспользованным до тех пор, пока не будет мусор Сборщик возвращает его обратно в пул, чего может не быть до тех пор, пока не появятся многочисленные другие запросы на подключение, которые в противном случае были бы вынуждены создавать новые подключения, даже если есть неиспользуемое, ожидающее сборки мусора.
Подробно о том, что сказал Крис Балланс, в разделе 15.13 спецификации C# (ECMA-334 версия 4) говорится: «Оператор using переводится на три части: получение, использование и удаление. Использование ресурса неявно включается в оператор try, который включает предложение finally. Это предложение finally удаляет ресурс. Если получен нулевой ресурс, то вызов Dispose не выполняется и исключение не генерируется. "
Описание близко к 2 страницам - стоит прочитать.
По моему опыту, SqlConnection / SqlCommand может генерировать ошибки настолько многими способами, что вам почти необходимо обрабатывать возникшие исключения больше, чем обрабатывать ожидаемое поведение. Я не уверен, что мне нужно здесь использовать предложение using, так как я хотел бы иметь возможность самостоятельно обрабатывать случай нулевого ресурса.
Здесь есть много отличных ответов, но я не думаю, что об этом еще сказано.
Независимо от того, что ... метод "Dispose" БУДЕТ вызываться для объекта в блоке "using". Если вы поместите оператор возврата или выдадите ошибку, будет вызвана команда «Dispose».
Пример:
Я создал класс MyDisposable, который реализует IDisposable и просто выполняет Console.Write. всегда записывает в консоль даже во всех этих сценариях:
using (MyDisposable blah = new MyDisposable())
{
int.Parse("!"); // <- calls "Dispose" after the error.
return; // <-- calls Dispose before returning.
}
Если ваш код выглядит так:
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, а не просто игнорировать исключение.
Если вызывающий вашу функцию отвечает за обработку любых исключений, оператор using - хороший способ обеспечить очистку ресурсов независимо от результата.
Он позволяет размещать код обработки исключений на границах слоя / сборки и помогает предотвратить чрезмерное загромождение других функций.
Конечно, это действительно зависит от типов исключений, создаваемых вашим кодом. Иногда вам следует использовать try-catch-finally вместо оператора using. Моя привычка - всегда начинать с оператора using для IDisposables (или иметь классы, содержащие IDisposables, также реализующие интерфейс) и при необходимости добавлять try-catch-finally.
В этом случае использование оператора using может оказаться бесполезным, если в любом случае у вас будет блок try / catch / finally. Как вы знаете, оператор using является синтаксическим сахаром для try / finally, который удаляет объект IDisposable. Если вы все равно собираетесь иметь свой собственный try / finally, вы, безусловно, можете сделать Dispose самостоятельно.
На самом деле это в основном сводится к стилю - вашей команде может быть удобнее использовать операторы using, или операторы using могут сделать код более чистым.
Но если шаблонный оператор using все равно будет скрыт, продолжайте и разберитесь со всем самостоятельно, если это ваше предпочтение.
Так что, в основном, «использование» - это то же самое, что «попробовать / поймать / наконец», только гораздо более гибкое для обработки ошибок.
Нет, Использование - это синтаксический сахар для оператора «попробуй / наконец». Если вы не заключите оператор в следующую команду try / catch или не вложите в нее, вы не сможете уловить какие-либо выброшенные исключения; что-то, что я считаю рискованным при взаимодействии с неуправляемыми ресурсами.
Это неправда, использование не является изящным способом замены try / catch / finally, потому что оно не дает вам возможности «перехватывать» исключения. Использование == Попробуйте / Наконец (без подвоха). Если вам все еще нужно перехватить исключение и выполнить некоторую индивидуальную обработку, вам нужно обернуть / вложить использование с помощью блока try / catch или полностью заменить его на 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» добавили бы опционально «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 ...
Как вам это нравится?
Я бы принял решение о том, когда и когда не использовать оператор 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
Я знаю, что лучше напишу. :-)
Я понимаю, что вы говорите, но не вижу преимущества перед блоком try catch finally с операторами close и dispose.