← Назад к вопросам

Во что разворачивается Dispose при использовании using?

2.0 Middle🔥 171 комментариев
#ASP.NET и Web API

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Во что разворачивается Dispose при использовании using?

using statement в C# — это синтаксический сахар для гарантированного вызова метода Dispose() на объектах, реализующих интерфейс IDisposable. Компилятор разворачивает using блок в try-finally конструкцию, которая гарантирует, что ресурсы будут освобождены даже при возникновении исключений.

Разворачивание using statement

Исходный код:

using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn);
    cmd.ExecuteReader();
}

Разворачивается в:

SqlConnection conn = new SqlConnection(connectionString);
try
{
    conn.Open();
    SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn);
    cmd.ExecuteReader();
}
finally
{
    if (conn != null)
    {
        ((IDisposable)conn).Dispose();
    }
}

Ключевые моменты

1. Гарантия вызова Dispose:

  • Dispose() вызывается всегда, даже если возникло исключение
  • finally блок выполняется в любом случае

2. Проверка null:

  • Компилятор добавляет проверку if (conn != null) перед вызовом Dispose()
  • Это защищает от исключений NullReferenceException

3. Кастинг к IDisposable:

  • Если переменная не типизирована как IDisposable, компилятор добавляет явный кастинг
  • Это позволяет работать с объектами, которые реализуют интерфейс неявно

Using declaration (C# 8.0+)

Новый синтаксис:

using SqlConnection conn = new SqlConnection(connectionString);
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn);
cmd.ExecuteReader();
// Dispose вызывается здесь, в конце метода

Разворачивается в:

SqlConnection conn = new SqlConnection(connectionString);
try
{
    conn.Open();
    SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn);
    cmd.ExecuteReader();
}
finally
{
    if (conn != null)
    {
        ((IDisposable)conn).Dispose();
    }
}

Множественные ресурсы

Исходный код:

using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn))
using (SqlDataReader reader = cmd.ExecuteReader())
{
    while (reader.Read())
    {
        Console.WriteLine(reader[0]);
    }
}

Разворачивается в (вложенные try-finally):

SqlConnection conn = new SqlConnection(connectionString);
try
{
    SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn);
    try
    {
        SqlDataReader reader = cmd.ExecuteReader();
        try
        {
            while (reader.Read())
            {
                Console.WriteLine(reader[0]);
            }
        }
        finally
        {
            if (reader != null)
            {
                ((IDisposable)reader).Dispose();
            }
        }
    }
    finally
    {
        if (cmd != null)
        {
            ((IDisposable)cmd).Dispose();
        }
    }
}
finally
{
    if (conn != null)
    {
        ((IDisposable)conn).Dispose();
    }
}

Обратите внимание: ресурсы освобождаются в обратном порядке относительно их создания (LIFO — Last In First Out).

Порядок вызова Dispose

Критично для зависимостей:

// reader зависит от cmd, cmd зависит от conn
// Освобождение: reader → cmd → conn

Это гарантирует, что при освобождении cmd, reader уже освобожден и не может обратиться к закрытому cmd.

Исключения при Dispose

using (var resource = new MyResource())
{
    // Если здесь выброшено исключение
    throw new InvalidOperationException();
    // Dispose все равно будет вызван
}

Если Dispose() выбросит исключение, оно заменит исходное исключение (это опасно).

IAsyncDisposable (C# 8.0+)

await using (var resource = new MyAsyncResource())
{
    // await resource.UseAsync();
}
// Разворачивается в try-finally с await DisposeAsync()

Лучшие практики

  • Всегда используй using для объектов, реализующих IDisposable
  • В конструкторах проверяй, нужна ли реализация IDisposable
  • Порядок важен — освобождай ресурсы в обратном порядке зависимостей
  • Исключения в Dispose — обрабатывай их корректно, не теряй оригинальное исключение
Во что разворачивается Dispose при использовании using? | PrepBro