Почему у 'using' нет блока catch? - PullRequest
10 голосов
/ 12 ноября 2009

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


Допустим, при создании объекта внутри параметра using создано исключение

 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? Есть ли лучший подход к этому, который я пропускаю?

 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
 }

Ответы [ 8 ]

27 голосов
/ 12 ноября 2009

Потому что вы бы «скрывали» дополнительные функции внутри несвязанного ключевого слова.

Однако вы всегда можете написать это так

using (...) try
{
}
catch (...)
{
}

И таким образом строка представляет ваши намерения - оператор использования, который также является попыткой

12 голосов
/ 12 ноября 2009

using Не имеет ничего общего с обработкой ошибок. Это сокращение от «Call Dispose, когда вы покидаете этот блок». Ваш второй пример кода вполне приемлем ... зачем связываться с тем, что работает?

8 голосов
/ 12 ноября 2009

Блок using - это просто синтаксический сахар для блока try-finally. Если вам нужно предложение catch, просто используйте try-catch-finally:

SqlConnection connection;
try 
{
    connection = new SqlConnection();
    connection.Open();
}
catch(Exception ex)
{
    // handle
}
finally 
{
    if (connection != null)
    {
        connection.Dispose();
    }
}

Да, это больше кода, чем ваш теоретический "using-catch"; Я считаю, что разработчики языка не считают это очень высоким приоритетом, и я не могу сказать, что когда-либо чувствовал его потерю.

5 голосов
/ 12 ноября 2009

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

Вместо этого мне нужно разрешить ему перейти на следующий уровень & mdash; обрабатывать его в функции, которая вызвала этот код, а не прямо там.

2 голосов
/ 12 ноября 2009

Вы смешиваете проблемы, по моему мнению. Управление ресурсами (т.е. удаление объектов) полностью отделено от обработки исключений. Отображение «один-к-одному», которое вы описываете в своем вопросе, представляет собой особый случай. Обычно обработка исключений не происходит в том месте, где заканчивается использование области. Или вы можете иметь несколько областей try-catch внутри блока using. Или ...

2 голосов
/ 12 ноября 2009

Интересная идея, но она может сбить с толку следующее:

 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?
 }
0 голосов
/ 30 июля 2012

Цель оператора «using» состоит в том, чтобы обеспечить выполнение некоторого типа операции очистки при выходе из блока кода, независимо от того, происходит ли этот выход из-за сбоя, исключения или return.Когда блок выходит из любого из этих средств, он вызовет Dispose с параметром using.Блок существует, в некотором смысле, в пользу того, что указывается в качестве параметра using, и, в общем, этой вещи не будет волновать, почему был завершен блок.

Есть несколько необычныхслучаи, для которых положения могут быть полезны;уровень их дополнительной полезности был бы намного ниже, чем при наличии using на первом месте (хотя, возможно, и лучше, чем некоторые другие функции, которые реализаторы считают нужным предоставить):

(1) Существует очень распространеннаяшаблон в конструкторах или фабриках объектов, которые инкапсулируют другие IDisposable объекты;если конструктор или фабрика выходят через исключение, инкапсулированные объекты должны быть 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 добавлять такие функции;первая и первая часть второй будут обрабатываться полностью на уровне языка.Последняя часть второго будет на уровне структуры.

0 голосов
/ 12 ноября 2009

Я рекомендую вам использовать пример № 1 и № 2 вместе взятых.Причина в том, что ваш оператор using может, например, прочитать файл и выдать исключение (например, File Not Found).Если вы не поймаете это, у вас есть необработанное исключение.Помещение блока try catch внутри блока using будет перехватывать только те исключения, которые возникают после выполнения оператора using.Сочетание вашего примера один и два лучше ИМХО.

...