Должен ли я Dispose () DataSet и DataTable? - PullRequest
186 голосов
/ 27 мая 2009

DataSet и DataTable оба реализуют IDisposable, поэтому, согласно общепринятым рекомендациям, я должен вызывать их методы Dispose ().

Однако из того, что я читал до сих пор, DataSet и DataTable на самом деле не имеют никаких неуправляемых ресурсов, поэтому Dispose () на самом деле ничего не делает.

Плюс, я не могу просто использовать using(DataSet myDataSet...), потому что DataSet имеет коллекцию DataTables.

Итак, чтобы быть в безопасности, мне нужно перебрать myDataSet.Tables, избавиться от каждого из DataTables, а затем избавиться от DataSet.

Так стоит ли вызывать Dispose () для всех моих DataSets и DataTables?

Добавление:

Для тех из вас, кто считает, что DataSet следует утилизировать: В общем случае шаблон для удаления должен использовать using или try..finally, потому что вы хотите гарантировать, что будет вызван Dispose ().

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

Или вы предлагаете мне просто вызвать myDataSet.Dispose () и забыть об удалении DataTables в myDataSet.Tables?

Ответы [ 11 ]

138 голосов
/ 27 мая 2009

Вот пара обсуждений, объясняющих, почему Dispose не является необходимым для DataSet.

Утилизировать или не утилизировать? :

Метод Dispose в DataSet существует ТОЛЬКО из-за побочного эффекта наследования - другими словами, он фактически не делает ничего полезного в финализации.

Должен ли метод Dispose вызываться для объектов DataTable и DataSet? включает некоторые пояснения из MVP:

Пространство имен system.data (ADONET) не содержит неуправляемые ресурсы. Поэтому нет необходимости выбрасывать до тех пор, пока вы не добавите в него что-то особенное.

Понимание метода Dispose и наборов данных? имеет комментарий от авторитета Скотта Аллена:

На практике мы редко располагаем набором данных, поскольку он дает мало преимуществ "

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

123 голосов
/ 22 октября 2009

Обновление (1 декабря 2009 г.):

Я хотел бы изменить этот ответ и признать, что первоначальный ответ был ошибочным.

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

Однако оказывается, что DataSets, DataViews, DataTables подавляют финализацию в своих конструкторах - вот почему вызов Dispose () для них явно ничего не делает.

Предположительно, это происходит потому, что у них нет неуправляемых ресурсов; поэтому, несмотря на то, что MarshalByValueComponent делает допуски для неуправляемых ресурсов, этим конкретным реализациям нет необходимости и, следовательно, они могут отказаться от завершения.

(То, что авторы .NET позаботятся о том, чтобы подавить финализацию для тех типов, которые обычно занимают больше всего памяти, говорит о важности этой практики в целом для финализуемых типов.)

Несмотря на то, что эти детали все еще недостаточно документированы с момента создания .NET Framework (почти 8 лет назад), довольно удивительно (что вы по сути оставлены на своих собственных устройствах, чтобы просеивать хотя и противоречивые, неоднозначные материалы для размещения время от времени это расстраивает, но обеспечивает более полное понимание структуры, на которую мы опираемся каждый день).

После долгих чтений вот мое понимание:

Если объект требует завершения, он может занимать память дольше, чем нужно - вот почему: a) Любой тип, который определяет деструктор (или наследует от типа, который определяет деструктор) считается финализуемым; б) При выделении (до запуска конструктора) указатель помещается в очередь финализации; c) Для финализуемого объекта обычно требуется 2 коллекции для восстановления (вместо стандартного 1); d) Подавление финализации не приводит к удалению объекта из очереди финализации (как сообщает! FinalizeQueue в SOS) Эта команда вводит в заблуждение; Знание того, какие объекты находятся в очереди финализации (само по себе), не помогает; Было бы полезно знать, какие объекты находятся в очереди завершения и все еще требуют завершения (есть ли команда для этого?)

Подавление финализации немного выключает заголовок объекта, указывая среде выполнения, что ему не нужно вызывать свой финализатор (не нужно перемещать очередь FReachable); Он остается в очереди Финализации (и по-прежнему сообщается! FinalizeQueue в SOS)

Все классы DataTable, DataSet, DataView имеют корни в MarshalByValueComponent, финализируемом объекте, который может (потенциально) обрабатывать неуправляемые ресурсы

  • Поскольку DataTable, DataSet и DataView не представляют неуправляемые ресурсы, они подавляют завершение в своих конструкторах
  • Хотя это необычный шаблон, он освобождает вызывающего абонента от необходимости беспокоиться о вызове Dispose после использования
  • Это, а также тот факт, что DataTable потенциально могут совместно использоваться различными DataSets, вероятно, поэтому DataSets не заботятся о том, чтобы избавиться от дочерних DataTables
  • Это также означает, что эти объекты появятся под! FinalizeQueue в SOS
  • Тем не менее, эти объекты должны быть восстановлены после одной коллекции, как и их не финализируемые аналоги

4 (новые ссылки):

Оригинальный ответ:

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

Без сомнения, Dispose должен вызываться для любых объектов Finalizable.

DataTables являются Завершается.

Calling Dispose значительно ускоряет восстановление памяти.

MarshalByValueComponent вызывает GC.SuppressFinalize (this) в его Dispose () - пропустить это означает, что нужно ждать десятки, если не сотни коллекций Gen0, прежде чем память будет освобождена:

С этим базовым пониманием завершения мы уже можно вывести некоторые очень важные вещи:

Во-первых, объекты, которые требуют доработки жить дольше, чем объекты, которые не делают. На самом деле они могут жить намного дольше. Например, предположим, что объект находится в gen2 должен быть завершен. Завершение будет запланировано, но объект все еще находится в gen2, поэтому он будет не будет повторно собран до следующего коллекция gen2 происходит. Это может быть очень долго, и, на самом деле, если дела пойдут хорошо, это будет долгое время, потому что коллекции gen2 являются дорогостоящими, и поэтому мы хотим, чтобы они случается очень редко. Старшая объекты, нуждающиеся в доработке, могут придется ждать десятки, если нет сотни коллекций gen0 перед их пространство освоено.

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

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

Возьмите его от кого-то, кто видел сотни МБ не ссылочных DataTables в Gen2: это чрезвычайно важно и совершенно пропущено ответами в этой теме.

Ссылка:

1 - http://msdn.microsoft.com/en-us/library/ms973837.aspx

2 - http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx

3 - http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

22 голосов
/ 27 мая 2009

Вы должны предположить, что он делает что-то полезное, и вызывать Dispose, даже если он ничего не делает в текущем. Воплощения NET Framework, нет никаких гарантий, что так будет и в будущих версиях, что приведет к неэффективному использованию ресурсов.

16 голосов
/ 27 мая 2009

Даже если у объекта нет неуправляемых ресурсов, утилизация может помочь GC, разбивая графы объектов. В общем случае, если объект реализует IDisposable, следует вызвать Dispose ().

То, что Dispose () действительно что-то делает или нет, зависит от данного класса. В случае DataSet реализация Dispose () наследуется от MarshalByValueComponent. Он удаляет себя из контейнера и вызывает событие Disposed. Исходный код ниже (разобран с помощью .NET Reflector):

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}
7 голосов
/ 27 мая 2009

Вы сами создаете таблицы данных? Потому что перебирать дочерние элементы любого объекта (как в DataSet.Tables) обычно не нужно, так как задача родителя - распоряжаться всеми дочерними элементами.

Как правило, правило таково: если вы создали его, и он реализует IDisposable, утилизируйте его. Если вы НЕ создали его, то НЕ утилизируйте его, это работа родительского объекта. Но у каждого объекта могут быть особые правила, см. Документацию.

Для .net 3.5 в явном виде написано «Утилизируйте его, когда он больше не используется», поэтому я бы так и сделал.

6 голосов
/ 27 мая 2009

Я вызываю dispose в любое время, когда объект реализует IDisposeable. Это там по причине.

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

обновление

Прошло 5 лет с тех пор, как я ответил на этот вопрос. Я все еще согласен с моим ответом. Если есть метод dispose, его следует вызывать, когда вы закончите с объектом. Интерфейс IDispose был реализован по причине.

4 голосов
/ 27 мая 2009

Если ваше намерение или контекст этого вопроса на самом деле является сборкой мусора, то вы можете установить для наборов данных и таблиц данных значение null в явном виде или использовать ключевое слово using и позволить им выйти из области видимости. Утилизация не так много, как сказал ранее Tetraneutron. GC будет собирать объекты набора данных, на которые больше нет ссылок, а также те, которые находятся вне области действия.

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

1 голос
/ 27 мая 2009

Наборы данных реализуют IDisposable основательно MarshalByValueComponent, который реализует IDisposable. Поскольку наборы данных управляются, нет никакой реальной выгоды от вызова dispose.

0 голосов
/ 11 октября 2018

Нет необходимости утилизировать () потому что DataSet наследует класс MarshalByValueComponent, а MarshalByValueComponent реализует интерфейс IDisposable

0 голосов
/ 20 ноября 2013

Попробуйте использовать функцию Clear (). Это прекрасно работает для меня.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
...