Поймать исключения в блоке использования за пределами используемого блока - что лучше?
Есть ли разница между этими элементами куска кода и какой подход лучше.
try
{
using()
{
//Do stuff
}
}
catch
{
//Handle exception
}
using()
{
try
{
//Do stuff
}
catch
{
//Handle exception
}
}
Ответы
Ответ 1
Существуют различия, но это сводится к тому, что используемый блок создает собственные блоки try и scope.
try
{
using(IDisposable A = GetDisposable())
{
//Do stuff
}
}
catch
{
//Handle exception
// You do NOT have access to A
}
using(IDisposable A = GetDisposable()) //exception here is uncaught
{
try
{
//Do stuff
}
catch
{
//Handle exception
// You DO have access to A
}
}
Ответ 2
Есть разница между этими блоками. Во втором случае исключение не будет поймано, если оно выбрано в строке using() (например, создание экземпляра объекта IDisposable и конструктор генерирует исключение). Какой из них лучше будет зависеть от ваших конкретных потребностей.
Ответ 3
Да. В первом случае ресурс, который вы используете, будет удален до того, как будет выполнен блок catch. В дальнейшем он будет удален позже. Более того, оператор "foo" не входит в объем предложения catch. Блок "использования" является почти синтаксическим сахаром, так что
using (foo)
{
}
является
try
{
foo;
}
finally
{
foo.Dispose();
}
Какое поведение "лучше" не очевидно без контекста.
Ответ 4
В конечном итоге вы можете комбинировать оба метода для преодоления обоих недостатков:
IFoo f;
try{
f = new Foo();
f.Bar();
catch{
// Do something exceptional with f
} finally{
if(f != null) f.Dispose();
}
Ответ 5
Предполагаю, вы имеете в виду:
using (var x = new Y(params))
{
}
В обоих случаях? Тогда очевидным отличием является область действия x. Во втором случае вы можете получить доступ к x в предложении catch. В первом случае вы не смогли.
Я также воспользуюсь возможностью, чтобы напомнить вам не "обрабатывать" исключение, если вы не можете действительно что-то сделать. Это включает в себя ведение журнала исключения, что было бы нормально, если только среда, в которой вы работаете, выполняет регистрацию для вас (как это делает ASP.NET 2.0 по умолчанию).
Ответ 6
Как упоминалось выше, только первый метод будет ловить исключения в инициализации объекта IDisposable и будет иметь объект в области для блока catch.
Кроме того, порядок операций для блоков catch и finally будет перевернут в зависимости от их вложенности. Возьмем следующий пример:
public class MyDisposable : IDisposable
{
public void Dispose()
{
Console.WriteLine("In Dispose");
}
public static void MethodOne()
{
Console.WriteLine("Method One");
using (MyDisposable disposable = new MyDisposable())
{
try
{
throw new Exception();
}
catch (Exception ex)
{
Console.WriteLine("In catch");
}
}
}
public static void MethodTwo()
{
Console.WriteLine("Method Two");
try
{
using (MyDisposable disposable = new MyDisposable())
{
throw new Exception();
}
}
catch (Exception ex)
{
Console.WriteLine("In catch");
}
}
public static void Main()
{
MethodOne();
MethodTwo();
}
}
Это напечатает:
Method One
In catch
In Dispose
Method Two
In Dispose
In catch
Ответ 7
Оператор using гарантирует, что Dispose вызывается, даже если возникает исключение, когда вы вызываете методы на объекте. Вы можете добиться того же результата, поставив объект внутри блока try и затем вызывая Dispose в блоке finally; на самом деле, это то, как оператор using транслируется компилятором.
using (Font font1 = new Font("Arial", 10.0f))
{
byte charset = font1.GdiCharSet;
}
то же, что и
{
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
((IDisposable)font1).Dispose();
}
}