Ограничения деструктора - доступ к управляемому члену из деструктора
Правильно ли, что в C# Destructor (Finalizer) вы не можете получить доступ к управляемым членам вашего класса? Если это правда, то почему? Какие еще ограничения финализатора C# вы знаете?
Пример:
class MyClass
{
private FileStream _fs;
private IntPtr _handle;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MyClass()
{
Dispose(false);
}
private void Dispose(bool isDisposing)
{
if (isDisposing)
{
_fs.Dispose(); // Won't be accessed from destructor
}
//some way to release '_handle' - this happans anyway (Called by Dispose or by Destructor)
}
}
3 ответа
Да, вы не должны получать доступ к другим управляемым классам из финализатора или из Dispose
когда этот метод вызывается финализатором. Ко времени выполнения финализатора объекта состояние любого управляемого объекта, на который он ссылался, является неопределенным. Они могут все еще существовать, могут ожидать завершения самостоятельно или, возможно, уже были удалены. Кроме того, финализаторы запускаются в другом потоке.
Это деталь реализации.NET 1.x, от которой было невероятно трудно избавиться. Частично проблема заключается в том, что большинство книг о.NET были написаны до 2005 года, у них всех есть глава о финализаторах, и все они совершенно ошибочно.
Грубо говоря, если вы считаете, что вам нужен финализатор, то вы ошибаетесь в 99,9% случаев. Этот финализируемый ресурс должен быть обернут в свой собственный класс, и у этого класса должен быть финализатор. Если вы думаете, что вам нужно реализовать шаблон Disposable, то вы ошибаетесь ~95% времени. Определенно неправильно здесь. Но иногда у вас нет выбора, если ваш базовый класс уже допустил ошибку при его реализации. В некоторых классах.NET Framework есть эта ошибка, которую Microsoft больше не может исправить.
Этот класс с финализатором в предыдущей главе - это не тот класс, который вы должны реализовать самостоятельно. Уже было написано, .NET 2.0 приобрела SafeHandle
классы. В лучшем случае вам может потребоваться получить собственный класс, если метод Release() не является тем, который уже предусмотрен в платформе. SafeBuffer полезен для ресурсов на основе указателей.
Большая, большая разница в том, что эти классы довольно особенные, у них есть критические финализаторы. Это дорогое слово, означающее, что они гарантированно будут работать, даже если поток финализатора бомбит необработанное исключение. На самом деле это не так важно в большинстве LOB-приложений: если они загораются, очистка ОС обычно достаточно хороша. Но важно, когда.NET-код выполняется на хосте, который имеет длительную гарантию работоспособности. SQL Server - хороший пример того, что его нужно перезагрузить, потому что слишком много взломанного кода SQL/CLR и утечка дескрипторов вредны для бизнеса.
Вы можете. Но какой в этом смысл? объект, в котором вы находитесь, стал недоступным, поэтому все эти управляемые ресурсы уже собираются GC. Поэтому вызывать dispose для них не нужно.
из MSDN
Использование деструкторов для освобождения ресурсов. Как правило, C# не требует столько управления памятью, сколько необходимо при разработке с языком, не предназначенным для среды выполнения со сборкой мусора. Это связано с тем, что сборщик мусора в.NET Framework неявно управляет выделением и освобождением памяти для ваших объектов. Однако, когда ваше приложение инкапсулирует неуправляемые ресурсы, такие как окна, файлы и сетевые подключения, вы должны использовать деструкторы для освобождения этих ресурсов. Когда объект подлежит уничтожению, сборщик мусора запускает метод Finalize объекта.
Явное освобождение ресурсов Если ваше приложение использует дорогой внешний ресурс, мы также рекомендуем вам предоставить способ явного освобождения ресурса до того, как сборщик мусора освободит объект. Вы делаете это путем реализации метода Dispose из интерфейса IDisposable, который выполняет необходимую очистку для объекта. Это может значительно улучшить производительность приложения. Даже с таким явным контролем над ресурсами деструктор становится защитой для очистки ресурсов в случае сбоя вызова метода Dispose.