Когда операторы using в C # наиболее полезны?

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

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

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

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?

person John B    schedule 15.04.2009    source источник
comment
То, что что-то выходит за рамки, не означает, что все ресурсы будут очищены. Если у вас есть дескрипторы неуправляемых объектов (скажем, COM-объектов), они будут потеряны, когда ваш управляемый объект умрет. Они будут торчать всю жизнь процесса, создавая утечки памяти.   -  person Rex M    schedule 15.04.2009
comment
В вашем примере вы явно указываете объекту на очистку, это полезно для подключений к базе данных или если вы держали блокировку файла. Если бы вы этого не сделали, финализатор объектов запустился бы позже (не специально после завершения метода), очищая его - но, возможно, слишком поздно.   -  person meandmycode    schedule 15.04.2009


Ответы (14)


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

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

Для получения дополнительной информации см. Эту статью о CodeProject: http://www.codeproject.com/KB/cs/tinguusingstatement.aspx

person Jeff Fritz    schedule 15.04.2009
comment
Таким образом, с точки зрения памяти объект все еще ожидает очистки ... но преимущество состоит в том, что любые ресурсы, которыми обладает объект, могут быть освобождены в его методе Dispose. Но имеет ли выход из вызова метода Dispose для объектов? - person John B; 15.04.2009
comment
Да, оператор return в середине блока using вызовет Dispose для объекта. Финализаторы и GC сработают позже в неопределенное время. - person Jeff Fritz; 15.04.2009
comment
Только если метод содержит using, и только для объектов, упомянутых в using. - person Chris Jester-Young; 15.04.2009
comment
Сама по себе функция не удаляет объекты детерминированным образом. Оператор using будет. - person Joel Coehoorn; 15.04.2009
comment
Технически объект должен иметь финализатор, который БУДЕТ запускаться в конечном итоге и закрывать соединение. - person meandmycode; 15.04.2009
comment
meandmycode: Да, но этого недостаточно; вы хотите, чтобы соединение было удалено (сразу), когда вы закончите. - person Chris Jester-Young; 15.04.2009
comment
Да, я отметил это в своем комментарии к вопросу - я просто указываю, что удаление не гарантирует закрытия, что бы ни случилось, закрытие произойдет независимо от удаления - просто ненадежно. - person meandmycode; 15.04.2009
comment
Соединения DBConnections, которые не удаляются сразу после использования, могут привести к истощению пула соединений и возникновению неприятной ошибки, связанной с невозможностью подключиться к базе данных. - person Jeff Fritz; 15.04.2009
comment
Ключ в конечном итоге против детерминированного. Для многих ресурсов (соединения с базой данных, сокеты, семафоры / мьютексы) в конечном итоге просто недостаточно. - person Joel Coehoorn; 15.04.2009
comment
Dispose будет вызываться без оператора using, но вы не знаете, когда. Объект можно было долго держать открытым. Оператор using обеспечивает немедленное закрытие, даже если возникает исключение. - person Joel Coehoorn; 15.04.2009
comment
using - это просто ярлык для try / finally. вы получаете ту же функциональность, это просто синтаксический сахар. - person Darren Kopp; 15.04.2009
comment
Хорошо, я думаю, что теперь понимаю это. Отличный разговор, ребята. Спасибо всем причастным! - person John B; 15.04.2009
comment
Технически Dispose не вызывается без оператора using, gc не знает об одноразовом, но он будет запускать финализатор, который по одноразовым рекомендациям вызовет согласованный защищенный метод, который также вызовет dispose, дополнительно указав, если вызов была утилизация или финализация - person meandmycode; 15.04.2009
comment
(для справки ^, поскольку некоторые люди считают, что метод Dispose () вызывается в gc) - person meandmycode; 15.04.2009

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

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

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

person Chris Jester-Young    schedule 15.04.2009
comment
Хотя это, вероятно, так, по правилам это не так. Сборщик мусора не вызывает сам Dispose, поэтому разработчик должен создать финализатор, который обеспечит очистку ресурсов, даже если Dispose не вызывается. - person Adam Robinson; 15.04.2009
comment
Соглашаться. Хотя хорошо написанные финализаторы будут делать то, что должны делать dispose. - person Chris Jester-Young; 15.04.2009

Этот:

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: объект удаляется, даже если возникает исключение. Также обратите внимание на дополнительный блок анонимной области видимости: это означает, что объект не только удаляется, но и выходит за пределы области видимости.

Другая важная вещь здесь - это то, что утилизация гарантированно произойдет сразу же. Это детерминированный. Без оператора using или аналогичной конструкции объект все равно выйдет из области видимости в конце метода и в конечном итоге может быть собран. В идеале ресурс должен быть уничтожен, чтобы система могла его восстановить. Но «в конце концов» может не произойти какое-то время, а «в идеале» и «будет» - очень разные вещи.

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

person Joel Coehoorn    schedule 15.04.2009
comment
+1 за указание на то, как использование контролирует область действия переменной! - person James Schek; 15.04.2009

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

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

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

person Peter Meyer    schedule 15.04.2009
comment
Он вообще не требует завершения. Он обеспечивает детерминированное удаление. Я думаю, что будет хорошей идеей держать в уме разделение доработки и утилизации. (Многие одноразовые типы вообще не имеют финализатора.) - person Jon Skeet; 15.04.2009
comment
Полностью согласен - плохой выбор терминов с моей стороны. Думаю, я отредактирую вопрос, чтобы отразить это. Финализаторы - это совершенно другая конструкция. - person Peter Meyer; 15.04.2009

«Использование» вступает в игру, когда ресурс необходимо удалить, и он реализует интерфейс IDisposable.

person Jose Basilio    schedule 15.04.2009

Когда метод завершается, не удаляется ли объект Font в любом случае?

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

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

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

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

person Richard    schedule 15.04.2009

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

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

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

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

person John Saunders    schedule 15.04.2009
comment
Не каждый раз. Приемлемая альтернатива - обернуть один или несколько IDisposable в класс, который сам реализует IDisposable. Затем этот класс размещает свои иждивенцы в собственном методе Dispose и финализаторе. - person Joel Coehoorn; 15.04.2009
comment
+1 хоть за IDisposable == прошу благополучно избавиться. Я бы даже сказал просить или требовать. - person Joel Coehoorn; 15.04.2009
comment
@Joel: если обернуты несколько, то отображается один IDisposable, который должен быть реализован в соответствии с этим шаблоном. Шаблон предназначен для созданных вами экземпляров, которые реализуют IDisposable. - person John Saunders; 15.04.2009

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

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

Таким образом, вам не нужно беспокоиться об исключении, из-за которого ваша переменная не будет удалена.

person Migol    schedule 15.04.2009
comment
на самом деле он также проверяет, что ссылка не равна нулю, перед вызовом Dispose () - person Lucero; 15.04.2009
comment
У использования есть еще одно преимущество. Он контролирует объем. Таким образом, вы не сможете попытаться использовать font1 после вызова dispose (). - person James Schek; 15.04.2009

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

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

person Andrew Hare    schedule 15.04.2009

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

person JP Alioto    schedule 15.04.2009

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

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

person Darren Kopp    schedule 15.04.2009

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

    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();
    }
person JasonRShaver    schedule 15.04.2009

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

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

person Lucero    schedule 15.04.2009