Утилизация конструктора Введенный объект - PullRequest
3 голосов
/ 30 декабря 2010

Допустим, у меня есть класс, который ассоциируется с другим классом. Это выглядело бы примерно так:

public class DisposableClassOne : IDisposable
{
   private class mDisposableClassTwo;

   public DisplosableClassOne(DisposableClassTwo dcTwoInjected)
   {
      mDisposableClassTwo = dcTwoInjected;
   }

   public void Dispose()
   {
      // Should I dispose here? or make caller dispose of dcTwoInjected
      //mDisposableClassTwo.Dispose();
   }
}

Должен ли я вызвать Dispose метод mDisposableClassTwo или я должен заставить вызывающего обработать его так?

using(DisposableClassTwo dcTwoInjected = new DisposableClassTwo())
using(DisposableClassOne dcOne = new DisposableClassOne(dcTwoInjected))
{
   // do stuff with dcOne
}

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

Ответы [ 6 ]

5 голосов
/ 30 декабря 2010

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

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

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


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

2 голосов
/ 30 декабря 2010

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

Является ли DisposableClassOne Одноразовым, ПОТОМУ ЧТО оно ссылается на DisposableClassTwo?

И,

a DisposableClassTwo имеют время жизни, не зависящее от DisposableClassOne?

Для меня ответы на эти два вопроса варьируются в зависимости от дизайна каждого класса.StreamReader / Writer - отличный пример того, что первый вопрос - «да», а второй - «нет» - никто не будет ожидать использовать Stream внутри StreamReader, как только Reader покончит с ним, поэтому Reader удалит его.

Но что, если DisposableClassTwo был каким-то другим ресурсом - может быть, ссылкой на файл, который вы передали нескольким классам по очереди, чтобы «сделать что-то».В этом случае вы не хотите, чтобы он утилизировался до тех пор, пока вы не будете готовы, и DisposableClassOne к тому времени может давно исчезнуть.

1 голос
/ 29 января 2011

Чтобы правильно использовать IDisposable объекты, нужно следовать принципу «Выключите ли последний свет, пожалуйста». Есть несколько хороших и неприятных сценариев:

  1. Создатель объекта передает его другому объекту («получателю»), но создатель первого объекта контролирует время жизни получателя и может знать, когда получателю больше не понадобится переданный объект. В этом случае создатель исходного объекта заботится об утилизации.
  2. Создатель объекта передает его получателю, и получатель знает, что никто не будет использовать объект после того, как он будет передан. В этом случае получатель вызовет Dispose, когда это будет сделано с объектом.
  3. Получатель объекта может иногда использоваться способами, которые соответствуют # 1, а иногда способами, которые соответствуют # 2, но поставщик объекта будет знать, когда ему сообщают, какая ситуация применима. Эту ситуацию можно решить, сообщив получателю, должен ли получатель распоряжаться предоставленным ему объектом.
  4. Как поставщик, так и получатель захотят использовать объект, но могут не знать, что переживет другой. Это самая сложная ситуация.

Мне нравится подход, предложенный в # 3. Иногда, однако, применяется ситуация № 4. Один из способов обработки для получателя объекта - инициировать событие, когда оно сделано с объектом. Это событие может Interlocked.Decrement счетчик, указывающий, сколько объектов все еще используют переданный объект. Как только этот счетчик достигнет нуля, объект может быть удален.

1 голос
/ 30 декабря 2010

Стандарт, с которым я знаком, - это использование одноразового шаблона . Иметь виртуальный Dispose(bool disposing) метод. Метод Dispose() вызывает Dispose(true), а финализатор вызывает Dispose(false).

Затем в основном методе утилизации:

if (disposing)
{
    // Dispose of the referenced object, as well.
}

StreamWriter и StreamReader следуют этому шаблону. Если вы явно позвоните Dispose(), они также удалят базовый Stream. Если вы позволите финализатору выполнить очистку, они оставят его в покое.

В качестве альтернативы, вы можете добавить свойство: DisposeChild, если true, удалить дочерний объект, если нет, оставить его в покое.

Я также согласен с rcravens. Практически при любых обстоятельствах объект должен находиться в той же области, в которой он был создан.

0 голосов
/ 03 октября 2016

Понятие «очистка владельцем» слишком распространено, чтобы его игнорировать. Собственность должна быть не просто совпадением.

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

Самый простой ответ - использовать два использования (), даже если вы затем пойдете и сделаете (и задокументируете!), Что второй IDisposable избавится от первого.

0 голосов
/ 30 декабря 2010

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

См. Эту ветку, в которой предлагается располагать только переменную-член:

Завершение / удаление шаблона в C #

...