When seeing about the whole finalizer/IDisposable issue, it is usual to see that, at the end, after all the long description, there will be something to the meaning of "LOL what I said was actually useless, you should use SafeHandle instead bye!" So I am wondering in what case does SafeHandle not fit, such that you have to resort to the finalizer/IDisposable old way?
Clearly, when the unmanaged resource you are wrapping is not acquired through a handle. Which is rare but not unheard of. An example would be writing wrappers in C++/CLI code, commonly done to wrap a native C++ class. The resource is then memory. The unmanaged kind.
Nevertheless, you can spend a career writing managed code and never write a finalizer. Finalizers belong in the framework classes.
When can't you use SafeHandle over Finalizer/IDisposable?
The obvious answer is almost never, Safehandles offer a lot of advantages.
But the code inside ReleasHandle()
must confirm to the constraints of a Constrained Execution Region (CER) . It can (should) not call code that could throw or lock. So if your cleanup is more complicated and 'unreliable' you still have to use a Finalizer/destructor.
But for (OS) handles you can and should always use a SafeHandle.
Many types of unmanaged resources can satisfy their cleanup responsibilities by using a simple API call on a handle. Such resources may (and often should) be usefully encapsulated within a SafeHandle. Some other types of unmanaged resources (e.g. event subscriptions held by long-lived publishers) have cleanup responsibilities which cannot very well be handled via finalizer. Finalizers may be useless if semantically-useless strong references keep abandoned objects alive, and may be unusable with resources which must be cleaned up by the thread which creates them. There's no need to write a custom finalizer for such resources, because no type of finalizer would eliminate the absolute 100% necessity of ensuring that they get deterministically disposed.
Some types of resources, however, may benefit from having a finalizer even though they need to do things that aren't allowed within a Constrained Execution Region. Finalizers may be useful in situations where a background thread is responsible for manipulating an object, and where the main application holds an object which in turn holds a reference to the real object. A finalizer on the main application's reference-holding object may signal the background thread that the object it's maintaining is no longer needed. Such signalling must be done carefully to ensure it won't break the finalizer thread, but if done with care it may be helpful to have it in a finalizer even if it wouldn't be allowed within a CER.
© 2022 - 2024 — McMap. All rights reserved.