Как бороться с классом, чем инкапсулирует одноразовый экземпляр?
interface IMyInterace
{
void Open();
object Read();
void Close();
}
class MyImplementation : IMyInterface
{
public void Open() { /* instantiates disposible class */ }
//...
public void Close() { /* calls .Dispose(); */ }
}
Есть ли хороший способ справиться с ситуацией такого типа, чтобы обеспечить вызов одноразовых экземпляров внутри класса? (Нет никаких сигналов для вызывающих абонентов, что они должны вызывать 'Close', кроме как в документации.) Реализации IMyInterface не обязательно инкапсулируют экземпляры IDisposible и закрываются и открываются повторно в течение всего времени жизни приложения.
Я думаю сделать это:
- Реализуйте IDisposible в MyImplementation.
- Установите Dispose() для вызова Close().
- Добавьте вызов Close () или Dispose() в начало Open, чтобы убедиться, что предыдущий вызов был закрыт.
Пользователи IMyInterface не знают, какую реализацию они используют, поэтому я не уверен, какую ценность имеет создание одноразового использования MyImplementation, и опять же, не все реализации будут инкапсулировать IDisposibles.
3 ответа
В дополнение к ответам уже здесь:
Если этот класс (часто / иногда) используется только через интерфейс, я бы посоветовал наследовать IMyInterace от IDisposable.
Это позволит вашим пользователям использовать эти объекты согласованно. Недостаток, конечно, в том, что вам может понадобиться добавить (фиктивные) методы Dispose к классам, которые на самом деле в этом не нуждаются. Но выгода заключается в согласованности и гибкости: что если класс в будущем изменится, и ему понадобится Dispose()?
Минимальный подход:
interface IMyInterace : IDisposable { }
sealed class MyImplementation : IMyInterface
{
public void Open() { /* instantiates disposible class */ }
public void Close() { /* calls _myField.Dispose(); */ }
public void Dispose() { Close(); } // only use this short form in a sealed class
}
Стандартный способ справиться с этим - просто MyImplementation
воплощать в жизнь IDisposable
,
Как уже упоминал Джон, твоя первая пуля в порядке.
Иногда Close()
метод функционально является синонимом Dispose()
и существует для поддержания семантической согласованности с абстракцией. То есть, чтобы дополнить Open()
метод. В других случаях, Close()
позволит вам снова открыть, но Dispose()
не следует. Таким образом, ваша вторая точка пули тоже в порядке.
Точка 3 не обязательно применима, потому что удаленный объект не должен использоваться повторно. Если вам нужно позвонить Open()
опять же, вам нужно использовать новый экземпляр. На самом деле, Open()
метод должен бросить ObjectDisposedException
один раз Dispose()
были вызваны (путем проверки частного disposed
логический флаг). Если вы хотите, чтобы объект поддерживал повторное открытие после закрытия, вы можете рассмотреть возможность использования Debug.Assert()
и / или выбрасывать исключение, если Open()
называется без Close()
, Это поможет предотвратить небрежное управление этими экземплярами.
Обязательно следуйте полной одноразовой схеме, которая более сложна, чем простая реализация интерфейса:
bool disposed;
public void Dispose() // don't make virtual!
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if(!disposed)
{
if(disposing)
{
// dispose of managed resources here, for example:
// if(resource != null) { resource.Dispose(); }
}
}
// dispose of unmanaged resources here
disposed = true;
}