Как известно, .NET Framework предоставляет объектам перед уничтожением "последнее желание", Finalize. К несчастью, многие плохо понимают чем такие вещи чреваты.
Правила:
1. Финализатор имеет смысл только если непосредственно ваш класс хранит в себе Handle внешнего ресурса (за редчайшим исключением)
2. Если вас потянуло на Finalize, постарайтесь переключиться на IDisposable, это и безвредно, и полезно
3. При переопределении метода Dispose(bool) выполняйте очистительные действия только если disposing==true (в противном случае очистку сделают и без вас)
Здравствуйте, mihailik, Вы писали:
M>Как известно, .NET Framework предоставляет объектам перед уничтожением "последнее желание", Finalize. К несчастью, многие плохо понимают чем такие вещи чреваты.
M><b>Финализаторы и деструкторы в C#</b>
M>Правила: M>1. Финализатор имеет смысл только если непосредственно ваш класс хранит в себе Handle внешнего ресурса (за редчайшим исключением)
M>2. Если вас потянуло на Finalize, постарайтесь переключиться на IDisposable, это и безвредно, и полезно
M>3. При переопределении метода Dispose(bool) выполняйте очистительные действия только если disposing==true (в противном случае очистку сделают и без вас)
А в чем заключаются провалы в методологии Рихтера, на Ваш вгляд, в контексте моделей особождения ресурсов?
R>А в чем заключаются провалы в методологии Рихтера, на Ваш вгляд, в контексте моделей особождения ресурсов?
Он очень много и увлекательно описывает финализаторы, не слишком заостряя внимание на том, что они
а) сложны и небезопасны
б) редко применимы
Кроме того, в частности:
По Рихтеру складывается впечатление, что Dispose КАК ПРАВИЛО реализуют одновременно с Finalize.
Рихтер описывает шаблон правильного совмещения IDisposable и Finalizable, не говоря о том, в каких случаях он применим.
* * *
В итоге, программисты-новички начинают думать о Finalize как о средстве, нередко необходимом в собственных классах. Наподобие "если класс инкапсулирует ресурс, ему нужен Dispose и Finalize". НЕВЕРНО!!!
Методологически правильно:
— показать, что Finalize существует;
— обяснить, зачем он существует;
— быстро и внятно объяснить, что ему редко есть применение в собственных классах; — и тут же уводить разговор к Dispose.
GZ>Ага, и выучить Dispose Pattern назубок. Сейчас как раз наблюдаю крутую очистку в финалайзере.
Хм, статья в MSDN сильно страдает теми же тараканами:
Во-первых, название Design Pattern сразу обманывает читателя.
Во-вторых, в тексте попросту НЕ ОБЪЯСНЯЕТСЯ, как Dispose связано с Finalize. Поэтому подразумевается, что это причинно-следственная связь. То есть, Design Pattern ТРЕБУЕТ Finalize? Ересь!
Здравствуйте, mihailik, Вы писали:
R>>А в чем заключаются провалы в методологии Рихтера, на Ваш вгляд, в контексте моделей особождения ресурсов?
M>Он очень много и увлекательно описывает финализаторы, не слишком заостряя внимание на том, что они M>а) сложны и небезопасны M>б) редко применимы
M>Кроме того, в частности:
M>По Рихтеру складывается впечатление, что Dispose КАК ПРАВИЛО реализуют одновременно с Finalize.
Ну... Каждый читает по-своему...
M>Рихтер описывает шаблон правильного совмещения IDisposable и Finalizable, не говоря о том, в каких случаях он применим.
M>* * *
M>В итоге, программисты-новички начинают думать о Finalize как о средстве, нередко необходимом в собственных классах. Наподобие "если класс инкапсулирует ресурс, ему нужен Dispose и Finalize". НЕВЕРНО!!!
M>Методологически правильно: M>— показать, что Finalize существует; M>— обяснить, зачем он существует; M>— быстро и внятно объяснить, что ему редко есть применение в собственных классах; M>— и тут же уводить разговор к Dispose.
Должен не согласиться, я как раз "новичек", прочитал недавно Рихтера, из выше сказанного в моей голове отразилось только указанное Вами в пункте "Методологически правильно". Может быть мне повезло!
M>>— и тут же уводить разговор к Dispose.
R>Должен не согласиться, я как раз "новичек", прочитал недавно Рихтера, из выше сказанного в моей голове отразилось только указанное Вами в пункте "Методологически правильно". Может быть мне повезло!
Спасибо, Роман.
Для меня это тоже полезный случай на заметку, хотя и нехарактерный.
Здравствуйте, mihailik, Вы писали:
GZ>>Ага, и выучить Dispose Pattern назубок. Сейчас как раз наблюдаю крутую очистку в финалайзере.
M>Хм, статья в MSDN сильно страдает теми же тараканами:
M>Во-первых, название Design Pattern сразу обманывает читателя.
M>Во-вторых, в тексте попросту НЕ ОБЪЯСНЯЕТСЯ, как Dispose связано с Finalize. Поэтому подразумевается, что это причинно-следственная связь. То есть, Design Pattern ТРЕБУЕТ Finalize? Ересь!
Вообще, я до сих пор не могу понять этих приплясываний с bool параметром.
почему нельзя сделать вот так:
public class BaseResource: IDisposable
{
private bool disposed = false;
public void Dispose()
{
if (!disposed)
{
DisposeManaged();
DisposeUnmanaged();
disposed = true;
GC.SuppressFinalize(this);
}
}
// Dispose managed resources.protected virtual void DisposeManaged()
{
Components.Dispose();
}
// Release unmanaged resources. protected virtual void DisposeUnmanaged()
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
// Do not provide destructors in types derived from this class.
~BaseResource()
{
DisposeUnmanaged(false);
}
public void DoSomething()
{
if(this.disposed)
{
throw new ObjectDisposedException();
}
}
}
// Design pattern for a derived class.
// Note that this derived class inherently implements the
// IDisposable interface because it is implemented in the base class.public class MyResourceWrapper: BaseResource
{
// A managed resource that you add in this derived class.private ManagedResource addedManaged;
// A native unmanaged resource that you add in this derived class.private NativeResource addedNative;
private bool disposed = false;
// Constructor for this object.public MyResourceWrapper()
{
// Insert appropriate constructor code here.
}
protected override void DisposeManaged()
{
addedManaged.Dispose();
base.DisposeManaged();
}
protected override void DisposeUnmanaged()
{
CloseHandle(addedNative);
base.DisposeUnmanaged();
}
}
По-моему, так гораздо яснее, и труднее сделать ошибку. Можно еще подумать на тему иерархии классов, где не все обязаны иметь оба типа ресурсов, нуждающихся в финализации.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S>По-моему, так гораздо яснее, и труднее сделать ошибку. Можно еще подумать на тему иерархии классов, где не все обязаны иметь оба типа ресурсов, нуждающихся в финализации.
Не посчитайте за мелкие придирки, но хотелось бы прояснить некоторые вещи...
Ну метод Dispose() c Finalize() сам по себе не связан никак. Связь определяет реалзиция программистом модели освобождения ресурсов. Я думаю, Вы именно это и имели в виду. Для полноты картины хотелось бы увидеть в листинге хотя бы псевдокод метода DisposeUnmanaged(bool disposing) . ИМХО, GC.SuppressFinalize(this); корректнее всатвить перед вызовами DisposeManaged(); DisposeUnmanaged();.
Так в чем же все-таки существенное упрощение? Ну вводится локальная переменная вместо bool параметра, отдельные виртуальные методы очистки управляемых и неуправляемых ресурсов, которые классы-наследники должны переопределить при необходимости, и все это надо будет держать в голове и реализовывать программисту, так же, как и в случае стандартного паттерна.
Все-таки то, что приводится в MSDN — паттерн, рекомендованный в общем случае, и, ИМХО, в этой связи сложно придумать что-либо более простое. Конечно же, то, что написано в MSDN, не является приказом, не подлежащим обсуждению, и ничто не мешает реализовать свою модель освобождения ресурсов, как в Вашем случае, и пользоваться ей. Единственное, что способствутет реализации программистом именно интерфейса IDisposable — это языковая поддержка (using, генерирующий блок try... finally с вызовом в последнем Dispose()), ну и само назначение этого интерфейса (реализованного во многих клссах FW) в контексте модели освобождения ресурсов.
P.S.
я бы добавил также lock в Dispose, и еще: throw new ObjectDisposedException(); компилироваться не будет
Здравствуйте, Alexander__S, Вы писали: A__>Не посчитайте за мелкие придирки, но хотелось бы прояснить некоторые вещи...
A__>Ну метод Dispose() c Finalize() сам по себе не связан никак. Связь определяет реалзиция программистом модели освобождения ресурсов. Я думаю, Вы именно это и имели в виду. Для полноты картины хотелось бы увидеть в листинге хотя бы псевдокод метода DisposeUnmanaged(bool disposing) .
Ну, это я опечатался. Нету у него никакого параметра. A__>ИМХО, GC.SuppressFinalize(this); корректнее всатвить перед вызовами DisposeManaged(); DisposeUnmanaged();.
A__>Так в чем же все-таки существенное упрощение? Ну вводится локальная переменная вместо bool параметра
Нет. Локальная переменная уже была. Просто вместо параметра с невнятной семантикой вводится два различных метода с, как мне кажется, более внятной семантикой.
Единственное различие — в МС-версии сначала освобождаются все ресурсы наследника, затем его предка и т.д. В моей версии сначала освобождаются все управляемые ресурсы, потом все неуправляемые. A__>, отдельные виртуальные методы очистки управляемых и неуправляемых ресурсов, которые классы-наследники должны переопределить при необходимости, и все это надо будет держать в голове и реализовывать программисту, так же, как и в случае стандартного паттерна.
Схема реализации каждого из методов — проще. A__>Все-таки то, что приводится в MSDN — паттерн, рекомендованный в общем случае, и, ИМХО, в этой связи сложно придумать что-либо более простое. Конечно же, то, что написано в MSDN, не является приказом, не подлежащим обсуждению, и ничто не мешает реализовать свою модель освобождения ресурсов, как в Вашем случае, и пользоваться ей. Единственное, что способствутет реализации программистом именно интерфейса IDisposable — это языковая поддержка (using, генерирующий блок try... finally с вызовом в последнем Dispose()), ну и само назначение этого интерфейса (реализованного во многих клссах FW) в контексте модели освобождения ресурсов.
С этим никто не спорит. A__>я бы добавил также lock в Dispose, и еще: throw new ObjectDisposedException(); компилироваться не будет
Это код от МС.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>По-моему, так гораздо яснее, и труднее сделать ошибку. Можно еще подумать на тему иерархии классов, где не все обязаны иметь оба типа ресурсов, нуждающихся в финализации.
Между тем, они уже есть... Например, флаг disposed из BaseResource в MyResourceWrapper уже не доступен. А объявленное поле никак не используется... DisposeUnmanaged(false) в финализаторе тоже никак не понятен.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
S>Нет. Локальная переменная уже была.
A__>>я бы добавил также lock в Dispose, и еще: throw new ObjectDisposedException(); компилироваться не будет S>Это код от МС.
Да, действительно. Извиняюсь за то, что не удосужился заглянуть в MSDN. Держал в голове рихтеровскую реализацию. Что ж, можно объявить конкурс на лучшую реализацию паттерна освобождения ресурсов
Здравствуйте, TK, Вы писали:
TK>Здравствуйте, Sinclair, Вы писали:
S>>По-моему, так гораздо яснее, и труднее сделать ошибку. Можно еще подумать на тему иерархии классов, где не все обязаны иметь оба типа ресурсов, нуждающихся в финализации.
TK>Между тем, они уже есть... Например, флаг disposed из BaseResource в MyResourceWrapper уже не доступен. А объявленное поле никак не используется... DisposeUnmanaged(false) в финализаторе тоже никак не понятен.
Виноват, писал с листа. Вот еще один подход:
public class BaseResource: IDisposable
{
private bool _disposed = false;
public bool Disposed {
[DebuggerStepThrough]
get { return _disposed; }
#region IDisposable implementation
public void Dispose()
{
if (!disposed)
{
DisposeManaged();
DisposeUnmanaged();
_disposed = true;
GC.SuppressFinalize(this);
}
}
#endregion// Dispose managed resources.protected virtual void DisposeManaged()
{
Components.Dispose();
}
// Release unmanaged resources. protected virtual void DisposeUnmanaged()
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
// Do not provide destructors in types derived from this class.
~BaseResource()
{
DisposeUnmanaged();
}
protected void CheckDisposed()
{
if(Disposed)
{
throw new ObjectDisposedException();
}
}
public void DoSomething()
{
CheckDisposed();
}
}
public class MyResourceWrapper: BaseResource
{
// A managed resource that you add in this derived class.private ManagedResource addedManaged;
// A native unmanaged resource that you add in this derived class.private NativeResource addedNative;
protected override void DisposeManaged()
{
addedManaged.Dispose();
base.DisposeManaged();
}
protected override void DisposeUnmanaged()
{
CloseHandle(addedNative);
base.DisposeUnmanaged();
}
}
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Hello, "Sinclair" > > TK>Между тем, они уже есть... Например, флаг disposed из BaseResource в > MyResourceWrapper уже не доступен. А объявленное поле никак не > используется... DisposeUnmanaged(false) в финализаторе тоже никак не > понятен. > Виноват, писал с листа. Вот еще один подход: >
Все равно, решение сложное
Например, нет атомарности освобождения ресурсов. Например, кто-то кидает
исключение в DisposeManaged. В результате до освобождения unmanaged ресурсов
дело уже не дойдет. В случае использования стандартного шаблона,
освобождение своих ресурсов так или иначе гарантировать...
Posted via RSDN NNTP Server 2.0
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, TK, Вы писали:
TK>Все равно, решение сложное
Хм... Оно если и не более простое, то все-таки более ООП-истое и более очевидное.
TK>В случае использования стандартного шаблона, TK>освобождение своих ресурсов так или иначе гарантировать...
... все равно не получится. В любом случае, если у тебя в Dispose() вылетит exception, то дальше ничего не пройдет.
И если уж этого опасаться, то в Тохином варианте всегда можно сделать
Hello, "Merle"
> TK>Все равно, решение сложное > Хм... Оно если и не более простое, то все-таки более ООП-истое и более > очевидное. >
Оно настолько очевидное, что более/менее правильный вариант удалось только с
третьего раза написать?
Кстати, если я хочу вызвать GC.ReRegisterForFinalize то, где это лучше
сделать, в DisposeUnmanaged? тоже, очевидное решение... Так же, действия в
финализаторе могут быть не обязательно связаны с unmanaged ресурсами...
Вобщем, название метода DisposeUnmanaged удачным назвать нельзя...
Да и потом, как узнать в DisposeUnmanaged что именно происходит с объектом —
был вызван Dispose или запустился финализатор. Флаг завести?
Posted via RSDN NNTP Server 2.0
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
TK>Оно настолько очевидное, что более/менее правильный вариант удалось только с TK>третьего раза написать? TK>Кстати, если я хочу вызвать GC.ReRegisterForFinalize то, где это лучше TK>сделать, в DisposeUnmanaged? тоже, очевидное решение... Так же, действия в TK>финализаторе могут быть не обязательно связаны с unmanaged ресурсами... TK>Вобщем, название метода DisposeUnmanaged удачным назвать нельзя...
TK>Да и потом, как узнать в DisposeUnmanaged что именно происходит с объектом — TK>был вызван Dispose или запустился финализатор. Флаг завести?
Вот именно эти проблемы-выкрутасы и привели к посту, с которого всё началось.
ИЗБЕГАЙТЕ ФИНАЛИЗАТОРОВ!
-заторов.. -аторов...
Finalize — это не пила, к которой есть стандартная техника безопасности. Скорее это ядерный реактор, который для каждого отдельного случая отдельно настраивается и продумывается.
Здравствуйте, TK, Вы писали:
TK>Оно настолько очевидное, что более/менее правильный вариант удалось только с TK>третьего раза написать?
Дело не в очевидности написания, а в очевидности использования.
TK>Кстати, если я хочу вызвать GC.ReRegisterForFinalize то, где это лучше TK>сделать, в DisposeUnmanaged?
Не, отдельную кнопку приделать.