Подтвердить что ты не робот

Как мне наследовать IDisposable?

Имена классов были изменены для защиты невинных.

Если у меня есть интерфейс с именем ISomeInterface. У меня также есть классы, наследующие интерфейс, FirstClass и SecondClass. FirstClass использует ресурсы, которые необходимо утилизировать. SecondClass не делает.

Итак, вопрос в том, где я должен унаследовать от IDisposable? Обе следующие опции кажутся менее идеальными:

1) Сделать FirstClass наследовать IDisposable. Тогда любой код, который имеет дело с ISomeInterfaces, должен знать, следует ли им распоряжаться. Это пахнет крепкой связью со мной.

2) Сделать ISomeInterface наследовать IDisposable. Тогда любой класс, который наследует от него, должен реализовать IDisposable, даже если ему нечего распоряжаться. Метод Dispose будет по существу пустым, за исключением комментариев.

# 2 кажется правильным выбором для меня, но мне интересно, есть ли альтернативы.

4b9b3361

Ответ 1

Если существует разумная вероятность того, что абстрактный объект (интерфейс или абстрактный класс), возможно, потребуется одноразово, он должен его реализовать. Stream, например, не < IDisposable, а IEnumerator<T>...

Абстрактный базовый класс может быть проще, так как вы можете иметь стандартную (пустую) реализацию Dispose(), а затем, возможно, шаблон finalizer/Dispose (bool), т.е.

void IDisposable.Dispose() { Dispose(true); GC.SuppressFinalize(this); }
protected virtual void Dispose(bool disposing) {}
~BaseType() {Dispose(false);}

Ответ 2

Если вы знаете, что некоторые реализации ISomeInterface требуют удаления, тогда интерфейс должен наследовать IDisposable, даже если конкретные реализации интерфейса не имеют ничего, чтобы избавиться.

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

Ответ 3

Это зависит от вашего интерфейса, но я склоняюсь к # 2. Если у вас есть две реализации ISomeInterface, и вам нужно только одно распоряжение, тогда вам нужно реорганизовать.

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

Ответ 4

Если вы хотите, чтобы весь ваш код имел дело с ISomeInterfaces в общем случае, тогда да, они все должны быть одноразовыми.

Если нет, тогда код, создающий FirstClass, должен утилизировать его:

using (FirstClass foo = new FirstClass()) {
    someObjectThatWantsISomeInterface.Act(foo);
}

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

public static void DisposeIfPossible(this object o) {
    IDisposable disp = o as IDisposable;
    if (disp != null)
        disp.Dispose();
}

// ...
someObject.DisposeIfPossible(); // extension method on object

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

Ответ 5

Мой совет - перейти к корню, а не непосредственно к конкретному классу. Пункт 2 относится к корню, и вы управляетесь каким-то контрактом от FirstClass. Если вы знаете, что классы должны реализовать какой-то интерфейс, то вы хотите убедиться, что интерфейс, который они подписывают, контракт iwth наследует IDisposable

Ответ 6

Все ответы, написанные до сих пор, пропускают ключевой момент: для базового типа или базового интерфейса требуется только реализовать IDisposable, если он вероятно, что код, который ожидает экземпляр базового класса, может в противном случае получить право собственности на экземпляр, который требует удаления, не осознавая этого. Наиболее распространенным сценарием, с помощью которого это может произойти, является метод factory; ярким примером является IEnumerable<T>/IEnumerator<T>. Большинство счетчиков не требуют очистки, но код, который вызывает IEnumerable<T>.GetEnumerator, как правило, не имеет особых оснований полагать, что возвращаемый счетчик действительно потребует очистки и не полагает, что он не будет. Как правило, быстрее реализовать все реализации IEnumerator<T> реализовать IDisposable, и все пользователи вызывают Dispose в возвращаемом счетчике, чем для того, чтобы потребители проверяли, реализует ли возвращаемый тип IDisposable и вызывает его, если это так.

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