Ответ 1
using
в основном эквивалентен:
try
{
// code
}
finally
{
obj.Dispose();
}
Таким образом, также имеет смысл вызывать Dispose()
, даже если необработанное исключение выбрано в коде внутри блока.
Насколько я понимаю, оператор using
в .NET вызывает метод IDisposable
object Dispose()
после кода выходит из блока.
Делает ли оператор using
что-то еще? Если нет, кажется, что следующие два примера кода достигают той же самой вещи:
Using Con as New Connection()
Con.Open()
'do whatever '
End Using
Dim Con as New Connection()
Con.Open()
'do whatever '
Con.Dispose()
Я дам лучший ответ тому, кто подтверждает, что я прав, или указывает, что я ошибаюсь и объясняю почему. Имейте в виду, что я знаю, что некоторые классы могут делать разные вещи в своих методах Dispose()
. Этот вопрос касается того, выполняет ли оператор using
тот же результат, что и вызов метода Dispose()
.
using
в основном эквивалентен:
try
{
// code
}
finally
{
obj.Dispose();
}
Таким образом, также имеет смысл вызывать Dispose()
, даже если необработанное исключение выбрано в коде внутри блока.
Как сказал Брайан Варшов в здесь, это просто импотент блоков try
и finally
, чтобы убедиться, что объект удален. Добавляя к его ответу; Блок using
также гарантирует, что объект будет удален, даже если вы вернете внутри с помощью области
Мне когда-то было любопытно об этом и проверил его, используя следующий подход:
Пользовательский тестовый класс IDisposable и главный
private class DisposableTest : IDisposable
{
public string Name { get; set; }
public void Dispose() { Console.WriteLine("{0}.Dispose() is called !", Name); }
}
public static void Main(string[] args)
{
try
{
UsingReturnTest();
UsingExceptionTest();
}
catch { }
try
{
DisposeReturnTest();
DisposeExceptionTest();
}
catch { }
DisposeExtraTest();
Console.ReadLine();
}
Реализация тестовых примеров
private static string UsingReturnTest()
{
using (DisposableTest usingReturn = new DisposableTest() { Name = "UsingReturn" })
{
return usingReturn.Name;
}
}
private static void UsingExceptionTest()
{
using (DisposableTest usingException = new DisposableTest() { Name = "UsingException" })
{
int x = int.Parse("NaN");
}
}
private static string DisposeReturnTest()
{
DisposableTest disposeReturn = new DisposableTest() { Name = "DisposeReturn" };
return disposeReturn.Name;
disposeReturn.Dispose(); // # IDE Warning; Unreachable code detected
}
private static void DisposeExceptionTest()
{
DisposableTest disposeException = new DisposableTest() { Name = "DisposeException" };
int x = int.Parse("NaN");
disposeException.Dispose();
}
private static void DisposeExtraTest()
{
DisposableTest disposeExtra = null;
try
{
disposeExtra = new DisposableTest() { Name = "DisposeExtra" };
return;
}
catch { }
finally
{
if (disposeExtra != null) { disposeExtra.Dispose(); }
}
}
И вывод:
//preceeding code
using (con = new Connection()) {
con.Open()
//do whatever
}
//following code
эквивалентно следующему (обратите внимание на ограниченный объем для con):
//preceeding code
{
var con = new Connection();
try {
con.Open()
//do whatever
} finally {
if (con != null) con.Dispose();
}
}
//following code
Это описано здесь: http://msdn.microsoft.com/en-us/library/yh598w02.aspx
Оператор using гарантирует, что Dispose вызывается, даже если Исключение возникает, когда вы вызываете методы на объекте. Вы можете добиться того же результата, поставив объект внутри блока try и затем вызов Dispose в блоке finally; на самом деле, именно так using оператор переводится компилятором.
Разница между ними заключается в том, что если исключение выбрано в
Con.Open()
'do whatever
Con.Dispose
не будет вызываться.
Я не в синтаксисе VB, но в С# эквивалентный код будет
try
{
con = new Connection();
// Do whatever
}
finally
{
if (con != null) con.Dispose();
}
A using
более ясен и более кратким, чем конструктор try...finally{Dispose()}
, и должен использоваться почти во всех случаях, когда вы не хотите разрешать вывод блока без вызова Dispose
. Единственными распространенными ситуациями, когда "ручное" удаление было бы лучше, было бы:
Обратите внимание, что при возврате IDisposable
из метода factory следует использовать что-то вроде следующего:
bool ok = false; DisposableClass myThing; try { myThing = new DisposableClass(); ... ok = true; return myThing; } finally { if (!ok) { if (myThing != null) myThing.Dispose(); } }
чтобы myThing
получал Dispose
d, если он не возвращается. Хотелось бы использовать метод using
наряду с некоторым методом "cancel Dispose", но такой вещи не существует.
Оператор using
гарантирует, что объект будет удален в случае возникновения исключения. Это эквивалент вызова dispose в блоке finally
.
Используемый блок гарантирует, что Dispose()
вызывается, если генерируется исключение.
Ваш второй образец этого не делает.
Если Con.Open()
выбрасывает исключение, в первом случае вам гарантируется вызов Con.Dispose()
. Во втором случае исключение распространяется вверх и Con.Dispose()
не будет вызываться.
Использование обертки вложенного блока в try/finally, который вызывает Dispose в блоке finally. Это гарантирует, что Dispose будет вызываться, даже если возникает исключение.
Вам следует использовать практически во всех случаях по соображениям безопасности
Если используется память, использование является гарантией того, что объект удаляется независимо от того, как выходит из него блок кода, который он окружает. Он делает это, окружая блок в попытке... наконец-то блокировать и проверять используемую переменную для nullness, а затем удалять ее, если она не равна нулю. Если было выбрано исключение, это позволило пузырить стек. Помимо этого, все, что он делает, - это гарантия удаления ненулевых одноразовых объектов.
try
{
var myDisposable = new DisposableObject();
myDisposable.DoSomething();
}
finally
{
if (myDisposable != null)
((IDisposable)myDisposable).Dispose();
}