Есть ли какая-то польза от реализации IDisposable на классах, которые не имеют ресурсов? - PullRequest
59 голосов
/ 28 февраля 2012

В C #, если у класса, такого как класс менеджера, нет ресурсов, есть ли какое-то преимущество в его наличии : IDisposable?

Простой пример:

public interface IBoxManager
{
 int addBox(Box b);
}

public class BoxManager : IBoxManager
{
 public int addBox(Box b)
 {
  using(dataContext db = new dataContext()){
   db.Boxes.add(b);
   db.SaveChanges();
  }
  return b.id;
 }
}

Будет ли какая-либо польза от использования памяти при использовании BoxManager, если он также реализует IDisposable?public class BoxManager : IBoxManager , IDisposable

Например:

BoxManager bm = new BoxManager();
bm.add(myBox);
bm.dispose();//is there benefit to doing this?

Ответы [ 11 ]

72 голосов
/ 28 февраля 2012

Существует только 2 причины для реализации IDisposable для типа

  • Тип содержит собственные ресурсы, которые должны быть освобождены, если тип больше не используется
  • Тип содержитполя типа IDisposable

Если ни то, ни другое не соответствует действительности, не следует реализовывать IDisposable

РЕДАКТИРОВАТЬ

Несколько человекупомянул, что IDisposable - это хороший способ реализовать начальные / конечные или зарезервированные операции.Хотя это не оригинальное намерение IDisposable, оно обеспечивает очень хороший шаблон.

class Operation {
  class Helper : IDisposable {
    internal Operation Operation;
    public void Dispose() {
      Operation.EndOperation();
    }
  }
  public IDisposable BeginOperation() {
    ...
    return new Helper() { Operation = this };
  }
  private void EndOperation() {
    ...
  }
}

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

public void BeginOperation(Action action) {
  BeginOperationCore();
  try {
    action();
  } finally {
    EndOperation();
  }
}
7 голосов
/ 28 февраля 2012

Нет разницы между одноразовой и одноразовой версией, если вы явно не используете метод Dispose().

6 голосов
/ 28 февраля 2012

Хотя ваш код не выиграет от реализации IDisposable, я не могу согласиться с другими мнениями, согласно которым IDisposable предназначен только для (прямо или косвенно) освобождения собственных ресурсов.IDisposable может использоваться всякий раз, когда объект должен выполнить задачу очистки в конце его жизненного цикла.Это чрезвычайно полезно вместе с using.

Очень популярный пример: в ASP.NET MVC Html.BeginForm возвращает IDisposable.При создании объект открывает тег, когда вызывается Dispose, он закрывает его.Никакие собственные ресурсы не задействованы, но все еще хорошо используют IDisposable.

5 голосов
/ 28 февраля 2012

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

4 голосов
/ 28 февраля 2012

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

(*), который может быть чем угодно, где угодно; возможно даже не на одном компьютере.

(**) или каким-либо образом, при котором поведение или состояние внешнего объекта изменяется

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

Чтобы учесть случай, когда объект был оставлен без предварительного вызова IDisposable.Dispose(), система позволяет объектам регистрировать "отказоустойчивый" метод очистки с именем Finalize(). Поскольку по какой-либо причине создатели C # не любят термин Finalize(), язык требует использования конструкции, называемой «деструктор», которая делает то же самое. Обратите внимание, что в общем случае Finalize() будет маскировать, а не решать проблемы, и может создавать собственные проблемы, поэтому его следует использовать с особой осторожностью, если вообще.

«Управляемый ресурс» обычно представляет собой имя, данное объекту, который реализует IDisposable и обычно, хотя и не всегда, реализует финализатор.

4 голосов
/ 28 февраля 2012

Нет, если нет (управляемых или неуправляемых) ресурсов, то IDisposable также не требуется.

Небольшое предупреждение: некоторые люди используют IDisposable для очистки обработчиков событий или больших буферов памяти, но

  • вы, похоже, не используете эти
  • это сомнительный шаблон в любом случае,
3 голосов
/ 29 февраля 2012

IDisposable также отлично подходит, если вы хотите использовать синтаксис using () {}.

В проекте WPF с ViewModels я хотел иметь возможность временно отключить NotifyPropertyChange события от возникновения. Чтобы быть уверенным, что другие разработчики снова включат уведомления, я написал немного кода, чтобы иметь возможность писать что-то вроде:

using (this.PreventNotifyPropertyChanges()) {
    // in this block NotifyPropertyChanged won't be called when changing a property value
}

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

public class MyViewModel {
    private volatile int notifyPropertylocks = 0; // number of locks

    protected void NotifyPropertyChanged(string propertyName) {
        if (this.notifyPropertylocks == 0) { // check the counter
            this.NotifyPropertyChanged(...);
        }
    }

    protected IDisposable PreventNotifyPropertyChanges() {
        return new PropertyChangeLock(this);
    }

    public class PropertyChangeLock : IDisposable {
        MyViewModel vm;

        // creating this object will increment the lock counter
        public PropertyChangeLock(MyViewModel vm) {
            this.vm = vm;
            this.vm.notifyPropertylocks += 1;
        }

        // disposing this object will decrement the lock counter
        public void Dispose() {
            if (this.vm != null) {
                this.vm.notifyPropertylocks -= 1;
                this.vm = null;
            }
        }
    }
}

Нет ресурсов для размещения здесь. Я хотел чистый код с неким синтаксисом try / finally. Ключевое слово using выглядит лучше.

3 голосов
/ 28 февраля 2012

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

Надеюсь, это поможет.

2 голосов
/ 29 февраля 2012

есть ли польза от этого: IDisposable?

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

Это одна из больших архитектурных проблем IDisposable, выделенных в IDisposable: что ваша мать никогда нерассказал вам об освобождении ресурса .По сути, если ваш класс не запечатан, вам нужно решить, будут ли ваши потомки иметь IDisposable членов.И это не то, что вы можете реально предсказать.

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

1 голос
/ 29 апреля 2012

Коротким ответом будет нет. Однако вы можете разумно использовать природу выполнения Dispose() в конце жизненного цикла объекта. Один из них уже привел хороший пример MVC (Html.BeginForm)

Я хотел бы отметить один важный аспект операторов IDisposable и using() {}. В конце оператора Using метод Dispose() автоматически вызывается с использованием объекта контекста (конечно, он должен реализовывать интерфейс IDisposable).

...