Я хотел найти способ разорвать цепочку IDisposable
, где какой-то вложенный класс, от которого вы внезапно зависите, теперь реализует IDisposable
, и вы не хотите, чтобы этот интерфейс волновал слои вашего композита. По сути, у меня слабые подписки на IObservable<T>
через 'SubscribeWeakly()', который я хочу очистить, когда уйду, чтобы не допустить утечки экземпляров оболочки на тот случай, если Observable
никогда не сработает. Это была мотивация, но я использую ее и для других целей.
В другом сообщении была аналогичная проблема, и ответ в основном указано, что вы все еще можете получить доступ к одноразовым элементам в вашем финализаторе. Однако вам не гарантируется, в каком порядке запускаются финализаторы, поэтому удаление может быть проблематичным.
Поэтому мне нужен был способ гарантировать, что одноразовый элемент останется живым, чтобы я мог вызвать Dispose()
в моем финализаторе. Итак, я посмотрел на GCHandle
, который позволяет C++ удерживать (и поддерживать в рабочем состоянии) управляемые объекты, втягивая их и их агрегаты в дескриптор приложения, чтобы поддерживать их в рабочем состоянии до тех пор, пока дескриптор не будет освобожден, а составное время жизни не вернется под контроль .NET. менеджер памяти. Исходя из C++, я подумал, что поведение, подобное std::unique_ptr
, будет хорошим, поэтому я придумал что-то похожее на AutoDisposer
.
public class AutoDisposer
{
GCHandle _handle;
public AutoDisposer(IDisposable disposable)
{
if (disposable == null) throw new ArgumentNullException();
_handle = GCHandle.Alloc(disposable);
}
~AutoDisposer()
{
try
{
var disposable = _handle.Target as IDisposable;
if (disposable == null) return;
try
{
disposable.Dispose();
}
finally
{
_handle.Free();
}
}
catch (Exception) { }
}
}
В классе, которому нужно распоряжаться ресурсами, когда он уходит, я бы просто назначил поле, например _autoDisposables = new AutoDisposer(disposables)
. Затем этот AutoDisposer
будет очищен сборщиком мусора вокруг них в то же время, что и содержащий его класс. Тем не менее, мне интересно, какие проблемы могут быть с этой техникой. Прямо сейчас я могу думать о следующем:
- Дополнительные накладные расходы на сборщик мусора из-за наличия финализаторов
- Дополнительные накладные расходы, связанные с тем, что .NET приходится извлекать элементы из управляемой памяти в дескрипторы приложений и возвращать их.
- Не подлежит модульному тестированию (я не могу предсказать, когда ресурс будет возвращен в .NET для управления памятью).
Поэтому я использую его экономно, когда реализация IDisposable
не слишком обременительна, если мне нужно детерминистически вызывать Dispose()
и т. д.
Кто-нибудь видит другие проблемы? Эта техника вообще действительна?
Dispose()
— это переворачивает все с ног на голову. - person Henk Holterman   schedule 01.08.2013IDisposable
(если он реализован правильно) может уже запустить свой финализатор и выполнить столько очистки, сколько можно разумно ожидать. внутри финализатора. Ваш вызовDispose
на этом этапе вряд ли поможет. - person Damien_The_Unbeliever   schedule 01.08.2013Dispose
никогда не вызывается. Так что я должен вызватьDispose
из моего финализатора. Я поддерживал его в рабочем состоянии, чтобы гарантировать отсутствие проблем при вызовеDispose
(висячие нулевые указатели, условия гонки и т. д.). - person moes   schedule 02.08.2013