Удалятся ли все объекты, созданные внутри строки в `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 вызывается для обоих объектов.)