In C++, I'm used to using the sentry pattern to ensure that resources acquired are properly released when the block or function exits (see here for example). I used this, for example, for grabbing the graphics state, and then I can do whatever I want to that state, and it gets put back when the sentry object (reference) goes out of scope.
Now I'm using C#, and the same trick doesn't work, because the destructor doesn't get called until who-knows-when later.
Is there some OTHER method that is guaranteed to fire when an object's last reference is released? Or do I just have to remember to call some Restore() or Apply() or DoYourThing() method before returning from any method where I would otherwise use a sentry?
C# has finalizers, like C++, that are called when an object is destroyed. They are in the same form as C++, that is:
~ClassName()
{
}
As you know, though, C# does not have deterministic memory management, and so this isn't really a great guarantee. AS a direct result, you should not use finalizers for releasing unmanaged resources in C#. Instead, we use the IDisposable
interface. This exposes a single method on the implementor, Dispose
, which is intended to be called when you want to release unmanaged resources.
public class MyDisposable : IDisposable
{
public void Dispose()
{
// get rid of some expensive unmanaged resource like a connection
}
}
We can also use the using
sugar to allow semantic invocation of Dispose()
when the using
block terminates (gracefully or not).
using(var disposable = new MyDisposable)
{
// Do something with the disposable
} // Dispose is invoked here
Should you find yourself using finalizers and Dispose
, you can consider using the GC.SuppressFinalize
method, although that's a bit out of my scope. There was a really good discussion elsewhere on StackOverflow about this here
This can be used to perform RAII-esque trickery, of course, such as
using(var guard = new Guard(resource))
{
}
C# provides the using
block for this case, which works on any object that implements IDisposable
.
For example, if you have a type class Foo : IDisposable
then you can do this:
using (new Foo(/* ... */)) {
// Your code that depends on this resource.
}
This is equivalent to:
Foo x = new Foo(/* ... */);
try {
// Your code
} finally {
if (x != null) {
((IDisposable)x).Dispose();
}
}
Except, of course, that you don't have access to x
. You can, however, gain such access if you need it by creating a variable in the using
block:
using (Foo foo = new Foo(/* ... */)) {
// Your code that uses "foo"
}
The standard way of creating a class that can be disposed of this way or by the garbage collector is:
class Foo : IDisposable {
protected virtual void Dispose(bool disposing) {
// Your cleanup code goes here.
}
public void Dispose() {
GC.SuppressFinalize(this);
Dispose(true);
}
~Foo() {
Dispose(false);
}
}
To implement deterministic deallocation of unmanaged resources, you would use a Dispose pattern. For many cases you can combine this with a using
block if you want this to occur within a specific scope.
File handles or handles to a graphics device would usually be considered a unmanaged resource. These are resources that the .NET framework does not track references for and does not automatically deallocate.
If it is managed resource, just references to C# objects/lists, then usually deterministic deallocation is not necessary, and the garbage collector can usually handle these more efficiently than you can. Don't worry so much about when managed resources are deallocated. If you no longer have a reference to them, then they should be of no concern. The concern comes when you are done with something but somehow have a lingering reference to it that is preventing the GC from deallocating it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With