Вопрос

У меня есть класс C# с Dispose функционировать через IDisposable.Он предназначен для использования внутри using блокировать, чтобы дорогостоящий ресурс, который он обрабатывает, мог быть немедленно освобожден.

Проблема в том, что ошибка произошла, когда раньше было выброшено исключение. Dispose был вызван, и программист пренебрег использованием using или finally.

В C++ мне никогда не приходилось об этом беспокоиться.Вызов деструктора класса будет автоматически вставлен в конец области видимости объекта.Единственный способ избежать этого — использовать оператор new и удерживать объект за указателем, но это не потребует от программиста дополнительной работы, а это не то, что он сделает случайно, например, забудет использовать using.

Есть ли способ для using блок для автоматического использования в C#?

Большое спасибо.

ОБНОВЛЯТЬ:

Я хотел бы объяснить, почему я не принимаю ответы финализатора.Эти ответы сами по себе технически верны, но они не являются деструкторами в стиле C++.

Вот ошибка, которую я нашел, сведенная к минимуму...

try
{
    PleaseDisposeMe a = new PleaseDisposeMe();
    throw new Exception();
    a.Dispose();
}
catch (Exception ex)
{
    Log(ex);
}

// This next call will throw a time-out exception unless the GC
// runs a.Dispose in time.
PleaseDisposeMe b = new PleaseDisposeMe();

С использованием FXCop — отличное предложение, но если это мой единственный ответ, мой вопрос должен стать призывом к людям, работающим с C#, или использовать C++.Двадцать вложенных операторов?

Это было полезно?

Решение

К сожалению, нет способа сделать это непосредственно в коде.Если это проблема внутри компании, существуют различные решения для анализа кода, которые могут выявить подобные проблемы.Вы изучали FxCop?Я думаю, что это позволит отловить такие ситуации и во всех случаях, когда объекты IDisposable могут остаться зависшими.Если это компонент, который используют люди за пределами вашей организации, и вы не можете потребовать FxCop, то документация — ваш единственный выход :).

Редактировать:В случае с финализаторами это на самом деле не гарантирует, когда произойдет финализация.Так что это может быть решением для вас, но это зависит от ситуации.

Другие советы

Там, где я работаю, мы придерживаемся следующих правил:

  • Каждый класс IDisposable должен иметь финализатор
  • Всякий раз, когда используется объект IDisposable, его необходимо использовать внутри блока «using».Единственное исключение — если объект является членом другого класса, и в этом случае содержащий его класс должен быть IDisposable и должен вызывать метод Dispose члена в своей собственной реализации Dispose.Это означает, что разработчик никогда не должен вызывать Dispose, кроме как внутри другого метода Dispose, что устраняет ошибку, описанную в вопросе.
  • Код в каждом финализаторе должен начинаться с журнала предупреждений/ошибок, уведомляющего нас о том, что финализатор был вызван.Таким образом, у вас есть очень хорошие шансы обнаружить описанные выше ошибки перед выпуском кода, плюс это может быть подсказкой об ошибках, возникающих в вашей системе.

Чтобы облегчить нашу жизнь, в нашей инфраструктуре также есть метод SafeDispose, который на всякий случай вызывает метод Dispose своего аргумента в блоке try-catch (с протоколированием ошибок) (хотя методы Dispose не должны генерировать исключения). ).

Смотрите также: Крис Лайонпредложения относительно IDisposable

Редактировать:@Ссорный:Вам следует вызвать GC.SuppressFinalize внутри Dispose, чтобы, если объект был удален, он не был «переутилизирован».

Также обычно желательно иметь флаг, указывающий, был ли объект уже удален или нет.Следующий шаблон обычно довольно хорош:

class MyDisposable: IDisposable {
    public void Dispose() {
        lock(this) {
            if (disposed) {
                return;
            }

            disposed = true;
        }

        GC.SuppressFinalize(this);

        // Do actual disposing here ...
    }

    private bool disposed = false;
}

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

@Ссорный

If будет вызываться, когда объект выводится за пределы области видимости и очищается сборщиком мусора.

Это утверждение вводит в заблуждение, и я неправильно его прочитал:Нет абсолютно никакой гарантии, когда будет вызван финализатор.Вы абсолютно правы, что billpg должен реализовать финализатор;однако он не будет вызываться автоматически, когда объект выходит за пределы области видимости, как он хочет. Доказательство, первый пункт под Операции завершения имеют следующие ограничения..

Фактически Microsoft предоставила Крису Селлсу грант на создание реализации .NET, которая использовала бы подсчет ссылок вместо сборки мусора. Связь.Как оказалось, существовал значительный хит производительности.

~ClassName()
{
}

РЕДАКТИРОВАТЬ (жирный шрифт):

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

«Принятый» шаблон удаления в соответствии с рекомендациями Framework выглядит следующим образом для неуправляемых ресурсов:

    public class DisposableFinalisableClass : IDisposable
    {
        ~DisposableFinalisableClass()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // tidy managed resources
            }

            // tidy unmanaged resources
        }
    }

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

Лучше всего использовать финализатор в своем классе и всегда использовать using блоки.

Однако на самом деле прямого эквивалента не существует, финализаторы выглядят как деструкторы C, но ведут себя по-другому.

Ты должен гнездиться using блоки, поэтому макет кода C# по умолчанию помещает их в одну строку...

using (SqlConnection con = new SqlConnection("DB con str") )
using (SqlCommand com = new SqlCommand( con, "sql query") )
{
    //now code is indented one level
    //technically we're nested twice
}

Когда вы не используете using в любом случае вы можете просто делать то, что он делает под капотом:

PleaseDisposeMe a;
try
{
    a = new PleaseDisposeMe();
    throw new Exception();
}
catch (Exception ex) { Log(ex); }  
finally {    
    //this always executes, even with the exception
    a.Dispose(); 
}

Благодаря управляемому коду C# очень хорошо заботится о своей памяти, даже если она плохо утилизирована.Если вы часто имеете дело с неуправляемыми ресурсами, это не так уж и важно.

Это ничем не отличается от того, как программист забывает использовать удалить в C++, за исключением того, что по крайней мере здесь сборщик мусора все равно его догонит.

И вам никогда не придется использовать IDisposable, если единственный ресурс, о котором вас беспокоит, — это память.Фреймворк справится с этим самостоятельно.IDisposable предназначен только для неуправляемых ресурсов, таких как подключения к базе данных, файловые потоки, сокеты и т. д.

Лучший вариант — заставить этот класс самостоятельно высвобождать дорогостоящий ресурс, прежде чем он будет утилизирован.

Например, если это соединение с базой данных, подключайтесь только при необходимости и немедленно отключайте его, задолго до того, как фактический класс будет удален.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top