Удалятся ли все объекты, созданные внутри строки в `use` statement?
На это можно ответить в другом месте, но после выполнения небольшого поиска я не нашел много информации по этому вопросу вне нормального контекста using
.
Мне любопытно, будут ли удалены все объекты, созданные в блоке using
, а также исходный объект.
Вот контекст:
Обычно я делал бы что-то вроде этого:
using (var conn = new SqlConnection(connectionString))
using (var cmd = new SqlCommand(commandText, conn))
{
//Do everything I need to here
}
Я знаю, что оба conn
и cmd
выходят за пределы области действия в данный момент и удаляются из-за прекрасного ключевого слова using
.
Мне любопытно, если бы следующие правила утилизации применимы к следующему утверждению:
using (var cmd = new (SqlCommand(commandText, new SqlConnection(ConnectionString)))
{
//Do everything I need here
}
Будет ли объект SqlConnection
, который был создан inline в статусе using
, удаляться, когда cmd
выходит за пределы области видимости и удаляется, потому что он связан с объектом?
Также будет синтаксически предпочтительным? Я лично считаю, что второй чище, но я понимаю, что читаемость также может сыграть здесь.
Ответы
Ответ 1
Для вашего второго кода Dispose
не будет вызываться в экземпляре SqlConnection
, когда поток покидает блок using
, если SqlCommand.Dispose()
не делает это внутри (и нет, это не так).
Согласно спецификации (8.13), using (ResourceType resource = expression) statement
преобразуется в:
{
ResourceType resource = expression;
try {
statement;
}
finally {
if(resource != null)
((IDisposable)resource).Dispose();
}
}
В вашем коде resource
есть SqlCommand
и что вызывается Dispose
.
Ответ 2
Нет.
Операторы using
применимы только к ресурсам, объявленным непосредственно в инструкции; а не другим выделениям в инициализаторе.
Для каждого ресурса требуется отдельный оператор using
.
Ответ 3
Согласно MSDN, этот код:
using (var temp = obj)
{
// ...
}
Переводит (включая дополнительные фигурные скобки для ограничения области):
{
var temp = obj;
try
{
// ...
}
finally
{
if (temp != null)
((IDisposable)temp).Dispose();
}
}
Как вы можете видеть, если вы замените obj
на new SqlCommand(commandText, new SqlConnection(ConnectionString))
, тогда будет правильно размещен только SqlCommand
.
{
SqlCommand temp = new SqlCommand(commandText,
new SqlConnection(ConnectionString));
try
{
// ...
}
finally
{
if (temp != null)
((IDisposable)temp).Dispose();
}
}
Таким образом, SqlConnection
не будет удаляться, если не будет найден расположенный SqlCommand
. Но это не так, и это не должно: он не создавал объект, поэтому он также не должен его уничтожать.
Ответ 4
Конечно, уже есть ответы, которые объясняют это правильно. Это описано в спецификации, упомянутой другими.
Но вы могли бы просто попробовать. Вот пример:
static class Program
{
static void Main()
{
using (new DisposeMe("outer", new DisposeMe("inner", null)))
{
Console.WriteLine("inside using");
}
Console.WriteLine("after using scope");
}
}
class DisposeMe : IDisposable
{
public readonly string name;
public DisposeMe(string name, object dummy)
{
this.name = name;
}
public void Dispose()
{
Console.WriteLine("'Dispose' was called on instance named " + name);
}
}
Вывод:
inside using
'Dispose' was called on instance named outer
after using scope
(Конечно, если вы вставляете два оператора using
, как в using (var inner = new DisposeMe("inner", null))
{
using (new DisposeMe("outer", inner))
{ ... } }
, метод Dispose
вызывается для обоих объектов.)