Когда используются слова С# "using"?

Таким образом, оператор using автоматически вызывает метод dispose для объекта, который "используется", когда вызывается блок использования, правильно?

Но когда это необходимо/полезно?

Например, скажем, у вас есть этот метод:

public void DoSomething()
{
    using (Font font1 = new Font("Arial", 10.0f))
    {
        // Draw some text here
    }
}

Нужно ли здесь использовать инструкцию using, поскольку объект создается в методе? Когда метод выходит, не удастся ли вообще удалить объект Font?

Или метод Dispose запускается в другое время после выхода метода?

Например, если метод был таким:

public void DoSomething()
{
    Font font1 = new Font("Arial", 10.0f);

    // Draw some text here
}

// Is everything disposed or cleared after the method has finished running?

Ответы

Ответ 1

Операция "using" наиболее полезна при работе с неуправляемыми объектами, такими как соединения с базой данных.

Таким образом, соединение закрывается и размещается независимо от того, что происходит в кодовом блоке.

Подробнее об этом читайте в статье CodeProject: http://www.codeproject.com/KB/cs/tinguusingstatement.aspx

Ответ 2

Без using (или ручного вызова Dispose()), объект в конечном итоге будет удален, просто не в детерминированное время. То есть, это может произойти сразу, через два дня или (в некоторых случаях) никогда.

Для таких вещей, как, скажем, сетевых подключений, вы хотите, чтобы соединение закрывалось, когда вы закончили с ним, а не "когда бы то ни было", в противном случае он будет работать в режиме ожидания при запуске сокета.

Кроме того, для таких вещей, как блокировки мьютексов, вы не хотите, чтобы те были выпущены "когда бы то ни было", иначе может возникнуть взаимоблокировка.

Ответ 3

Это:

public void DoSomething()
{
    using (Font font1 = new Font("Arial", 10.0f))
    {
        // Draw some text here
    }
}

отображается непосредственно на это:

public void DoSomething()
{
    {
        Font font1;
        try
        {
            font1 = new Font("Arial", 10.0f);
            // Draw some text here
        }
        finally
        {
            IDisposable disp = font1 as IDisposable;
            if (disp != null) disp.Dispose();
        }
    }
}

Обратите внимание на блок finally: объект расположен даже в случае возникновения исключения. Также обратите внимание на дополнительный анонимный блок видимости: это означает, что не только объект размещен, но и выходит за рамки.

Другая важная вещь заключается в том, что утилизацию гарантируют сразу. Он детерминирован. Без использования оператора или аналогичной конструкции объект по-прежнему выходит из области видимости в конце метода и затем может быть собран в конечном итоге. Тогда в идеале ресурс будет уничтожен, поэтому система может быть восстановлена. Но "в конце концов" может не произойти какое-то время, и "идеально" и "будет" - это совсем другие вещи.

Следовательно, "в конечном итоге" не всегда достаточно хорошо. Такие ресурсы, как соединения с базой данных, сокеты, семафоры/мьютексы и (в данном случае) ресурсы GDI часто сильно ограничены и их необходимо сразу очистить. Оператор using убедится, что это произойдет.

Ответ 4

Конструкция using обеспечивает детерминированное удаление - то есть освобождение ресурсов. В приведенном выше примере да, если вы не используете инструкцию "using", объект будет удален, но только если рекомендованный одноразовый шаблон (то есть утилизация ресурсов из финализатора класса, если это применимо) был реализован для рассматриваемого класса (в вашем примере - класс Font). Следует отметить, что используемая конструкция может использоваться только с объектами, реализующими интерфейс IDisposable. Это наличие этого интерфейса на объекте, который позволяет использовать для вызова метода Dispose.

Кроме того, базовые ресурсы будут выпущены только тогда, когда сборщик мусора решает собрать ваш объект шрифта вне сферы видимости. Ключевая концепция в программировании .Net(и большинство языков с сборщиком мусора) заключается в том, что только потому, что объект выходит за пределы области действия, это не означает, что он финализирован/выпущен/уничтожен и т.д. Скорее, сборщик мусора выполнит очистку, в то время, когда он определяет - не сразу, когда объект выходит за рамки.

Наконец, оператор using "запекает" конструкцию try/finally, чтобы гарантировать, что Dispose вызывается независимо от содержащегося кода, бросающего исключение.

Ответ 5

"Использование" вступает в игру, когда ресурс должен быть удален, и он реализует интерфейс IDisposable.

Ответ 6

Здесь то, что использует действительно (на основе вашего примера)

Font font1 = new Font(...);
try
{
    // code that uses font...
}
finally
{
    if (font1 != null)
        font1.Dispose();
}

Поэтому вам не нужно беспокоиться об исключении, чтобы ваша переменная не была удалена.

Ответ 7

Когда метод выходит, не удастся ли вообще удалить объект Font?

Нет, он станет неприемлемым и, следовательно, будет иметь право на сбор мусора. Если что-то другое (например, ссылка, содержащаяся в другой структуре данных) не поддерживается ссылкой.

Или метод Dispose запускается в другое время после выхода метода?

Да, в процессе, не выделяющем много памяти, которое могло бы быть значительным временем после выхода метода.

Сбор мусора по своей природе асинхронен и ленив, что делает его отличным способом обеспечения освобождения памяти, если память не слишком ограничена; но плохой для почти любого другого ресурса.

Ответ 8

Мне кажется очень простым.

Если класс реализует IDisposable, то он почти просит вас позвонить Dispose в любой созданный вами экземпляр, как только вы закончите с ним. Это шаблон, который я предлагаю использовать, всякий раз, когда создается такой экземпляр:

using (var instanceName = new DisposableClass())
{
    // Your code here
}

Он прост, он чист, и он работает для всего, кроме классов прокси WCF, которые не работают. Для них см. http://www.iserviceoriented.com/blog/post/Indisposable+-+WCF+Gotcha+1.aspx.

Ответ 9

Они полезны в любой ситуации, когда вы хотите, чтобы код очистки вызывался на объект детерминистически и независимо от исключений (так как оператор using действительно просто try/finally).

Чаще всего они используются для очистки неуправляемых ресурсов, но вы можете использовать их, как вы считаете нужным.

Ответ 10

Это чисто синтаксический сахар поверх блока try-catch-finally и вызов метода Dispose. Он даже не обязательно определяет лексическую область, поскольку вы можете передать ей переменную экземпляра. По сути, это делает ваш код более чистым и удобным в обслуживании.

Ответ 11

Dispose вызывается, когда он выходит из оператора using, потому что он явно вызван этой конструкцией. Метод dispose не вызывается явно, когда переменная выходит за пределы области (метод выхода).

Поведение, которое вы МОЖЕТ наблюдать, похоже на то, что обычно вещи, реализующие IDisposable, также вызывают метод Dispose в деструкторе классов, а деструктор COULD вызывается вскоре после того, как переменная выходит за пределы области действия, но не гарантируется. Деструктор вызывается сборщиком мусора.

Ответ 12

Мне нравится использовать их вот так:

    static public void AddSampleData(String name)
    {
        String CreateScript = GetScript(String.Format("SampleData_{0}", name));
        using (IDbConnection MyConnection = GetConnection())
        {
            MyConnection.Open();
            IDbCommand MyCommand = MyConnection.CreateCommand();
            foreach (String SqlScriptLine in CreateScript.Split(';'))
            {
                String CleanedString = SqlScriptLine.Replace(";", "").Trim();
                if (CleanedString.Length == 0)
                    continue;

                MyCommand.CommandText = CleanedString;
                int Result = MyCommand.ExecuteNonQuery();
            }
        }
    }

Они отлично работают для случаев, когда вы не хотите вводить код проверки ошибок, только очищает и пропускает ошибку. Альтернативный метод будет выглядеть следующим образом:

    static public void AddSampleData(String name)
    {
        String CreateScript = GetScript(String.Format("SampleData_{0}", name));

        IDbConnection MyConnection = null;
        try
        {
            IDbConnection MyConnection = GetConnection();
            MyConnection.Open();
            IDbCommand MyCommand = MyConnection.CreateCommand();
            foreach (String SqlScriptLine in CreateScript.Split(';'))
            {
                String CleanedString = SqlScriptLine.Replace(";", "").Trim();
                if (CleanedString.Length == 0)
                    continue;

                MyCommand.CommandText = CleanedString;
                int Result = MyCommand.ExecuteNonQuery();
            }
        }
        finally
        {
            if (MyConnection != null
                && MyConnection.State == ConnectionState.Open)
                MyConnection.Close();
        }
    }

И, честно говоря, какой метод легче читать? То же самое с формами:

    public void ChangeConfig()
    {
        using (ConfigForm MyForm = new ConfigForm())
        {
            DialogResult ConfigResult = MyForm.ShowDialog();
            if (ConfigResult == DialogResult.OK)
                SaveConfig();
        }

        ConfigForm MyForm = new ConfigForm();
        DialogResult ConfigResult = MyForm.ShowDialog();
        MyForm.Dispose();
        if (ConfigResult == DialogResult.OK)
            SaveConfig();
    }

Ответ 13

Все ответы о детерминистских и недетерминированных и о том, как using работают под капотом, верны.

Но, чтобы выбрать ваш конкретный образец, не забывайте, что почти все объекты System.Drawing(GDI + wrapper) также содержат ссылки на неуправляемую память, так что вы можете столкнуться с трудностями при их использовании без правильного их использования (где using является самым простым способом).

Ответ 14

Я задал очень похожий вопрос:

Ресурсы, которые необходимо очистить вручную на С#?

И я получил хороший совет в виде:

В общем случае, если что-то имеет метод Dispose, вы должны вызвать его, когда закончите с ним, или, если хотите, завершите его в оператор using.