Почему "использование" не имеет блока catch?
Я понимаю, что точка "использования" заключается в том, чтобы гарантировать, что будет вызываться метод Dispose объекта. Но как должно обрабатываться исключение в инструкции "using" ? Если есть исключение, мне нужно обернуть мой оператор "using" в try catch. Например:
Допустим, что существует исключение, созданное при создании объекта внутри параметра использования
try
{
// Exception in using parameter
using (SqlConnection connection = new SqlConnection("LippertTheLeopard"))
{
connection.Open();
}
}
catch (Exception ex)
{
}
Или Исключение в области использования
using (SqlConnection connection = new SqlConnection())
{
try
{
connection.Open();
}
catch (Exception ex)
{
}
}
Кажется, что если мне уже нужно обработать исключение с помощью try catch, возможно, я должен просто обработать удаление объекта. В этом случае выражение "using" , похоже, не помогает мне вообще. Как правильно обработать исключение с помощью инструкции "using" ? Есть ли лучший подход к этому, который мне не хватает?
SqlConnection connection2 = null;
try
{
connection2 = new SqlConnection("z");
connection2.Open();
}
catch (Exception ex)
{
}
finally
{
IDisposable disp = connection2 as IDisposable;
if (disp != null)
{
disp.Dispose();
}
}
Может ли синтаксис синтаксиса "using" быть немного более сладким...
Конечно, было бы неплохо иметь это:
using (SqlConnection connection = new SqlConnection())
{
connection.Open();
}
catch(Exception ex)
{
// What went wrong? Well at least connection is Disposed
}
Ответы
Ответ 1
У меня были места, где это было бы полезно. Но чаще, когда я хочу это сделать, оказывается, что проблема в моем дизайне; Я пытаюсь обработать исключение в неправильном месте.
Вместо этого мне нужно разрешить ему перейти на следующий уровень — обрабатывайте его в функции, которая называется этот код, а не там.
Ответ 2
Потому что вы будете "скрывать" дополнительные функции внутри ключевого слова, не связанного с ним.
Однако вы всегда можете написать это так
using (...) try
{
}
catch (...)
{
}
И таким образом строка представляет ваши намерения - оператор using, который также является попыткой
Ответ 3
using
Не имеет никакого отношения к обработке ошибок. Это сокращает "call Dispose, когда вы покидаете этот блок". Ваш второй пример кода вполне приемлем... почему беспорядок с тем, что работает?
Ответ 4
Блок использования - это просто синтаксический сахар для блока try-finally. Если вам нужно предложение catch, просто используйте try-catch-finally:
SqlConnection connection;
try
{
connection = new SqlConnection();
connection.Open();
}
catch(Exception ex)
{
// handle
}
finally
{
if (connection != null)
{
connection.Dispose();
}
}
Да, это больше кода, чем ваш теоретический "использование-catch"; Я сужу, что разработчики языка не считали это очень высоким приоритетом, и я не могу сказать, что когда-либо ощущал его потерю.
Ответ 5
Интересная идея, но это создало бы следующую путаницу:
using (SqlConnection connection = new SqlConnection())
using (SqlCommand cmd = new SqlCommand())
{
connection.Open();
}
catch(Exception ex)
{
// Is connection valid? Is cmd valid? how would you tell?
// if the ctor of either throw do I get here?
}
Ответ 6
На мой взгляд, вы смешиваете проблемы. Управление ресурсами (т.е. Удаление объектов) полностью отделено от обработки исключений. Индивидуальное сопоставление, которое вы описываете в своем вопросе, является просто особым случаем. Обычно обработка исключений не будет происходить в том же месте, где заканчивается область использования. Или вы можете иметь несколько областей try-catch внутри используемого блока. Или...
Ответ 7
Я рекомендую вам использовать пример # 1 и # 2 вместе. Причина в том, что ваш оператор использования может, например, прочитать файл и выбросить исключение (например, файл не найден). Если вы его не поймаете, у вас есть необработанное исключение. Помещение блока catch try внутри используемого блока будет только улавливать исключения, которые возникают после выполнения инструкции using. Комбинация вашего примера одна и вторая лучше всего ИМХО.
Ответ 8
Цель оператора "Использовать" состоит в том, чтобы гарантировать, что будет выполняться какой-либо тип операции очистки, когда выполнение завершает блок кода, независимо от того, проходит ли этот выход через провал, исключение или return
. Когда блок выйдет через любое из этих средств, он вызовет Dispose
по параметру using
. Блок существует, в некотором смысле, в интересах того, что указывается как параметр using
, и вообще, что все равно, почему этот блок был удален.
Есть несколько необычных случаев, для которых положения могут быть полезными; их уровень дополнительной полезности будет значительно ниже, чем при условии using
в первую очередь (хотя, возможно, лучше, чем некоторые другие функции, которые разработчики считают нужным):
(1) Существует очень распространенный шаблон в конструкторах или фабриках объектов, которые инкапсулируют другие объекты IDisposable
; если конструктор или factory выходит из исключения, инкапсулированные объекты должны быть Dispose
d, но если он выходит через return
, они не должны. В настоящее время такое поведение должно быть реализовано с помощью try
/catch
или путем объединения try
/finally
с флагом, но это было бы полезно, если бы существовал либо вариант using
, который вызывал бы только вызов Dispose
, когда он вышел из-за исключения, или же оператор keep using
, который исключает временное использование оператором using
для хранения объекта, который требует удаления (поскольку using
не может предшествовать идентификатор, такой функция может быть добавлена способом, несколько аналогичным yield return
).
(2) В некоторых случаях было бы полезно, если ключевое слово finally
было расширено для принятия аргумента Exception
; он будет содержать исключение, из-за которого защищенное предложение выйдет (если есть) или null
, если охраняемое предложение выходит из строя (через возврат или провал), и если блок using
может использовать interface IDisposeExOnly {void DisposeEx(Exception ex);}
и Interface IDisposeEx : IDisposable, IDisposableExOnly {}
(во время компиляции, выбранных DisposeEx()
, если они реализованы, или Dispose()
в противном случае). Это может позволить объектам на основе транзакций безопасно поддерживать автоматическую фиксацию (т.е. Выполнить фиксацию, если исключение прошедшего отказа null
или возврат назад, если не нуль), а также позволит улучшить регистрацию в ситуациях, когда Dispose
не удается как следствие проблемы в защищенном предложении (правильная вещь для Dispose
должна генерировать исключение, которое инкапсулирует как исключение, которое было ожидающим, когда оно было вызвано, так и исключение, которое произошло как следствие, но там в настоящее время нет чистого способа сделать это).
Я не знаю, добавит ли Microsoft такие функции; первая и первая часть второй, будут полностью обрабатываться на уровне языка. Последняя часть второй будет на уровне Framework.