Born said:
I've checked WeakReference. It seems a good alternative to handles although
my MSDN 2005 says an exception will be triggered if the referenced object
has finalized ( I did not experience any exception in my test).
This may help... this is how I think of WeakReference.
For any object, there are two kinds of references you can hold to it:
strong references that require that the object be kept alive in order
that the references be useful, and weak references that will use the
object if it's available but don't particularly mind if it disappears.
Most references are of the first type, strong. Some references,
particularly certain types of event subscriptions, are conceptually of
the second type.
For example, I use weak references to subscribe to static events. I
want the object to be notified when, say, the user changes the screen
resolution, but I don't particularly care if the event occurs and
discovers that the object is gone. Or, from the other angle, it's not
worth keeping the object alive just to receive this kind of event.
The trick, as Marc pointed out, is to design your program so that every
resource has exactly one (strong reference) "owner" at any time, so
that it can be disposed when that owner is finished with it. Ownership
may pass from one owner to another, but in the end you know whose
responsibility it is to dispose of the resource. If you can manage to
make every other reference except the owner a "weak reference" then
you're done.
Sometimes, however, this isn't possible. Sometimes you really do have
multiple owners of a resource. In that case you have to implement some
sort of reference counting scheme like in C++. The only bummer is that
in C# you can't invoke code upon assignment so you can't do auto-magic
reference counting. You can do something close, though. You could use a
secondary, IDisposable object to hold a reference to your resource,
then use "using" to ensure that the reference counting would be correct
no matter what. Something like this:
public class ReferenceToResource : IDisposable
{
private Resource _res;
private bool _disposed = false;
public ReferenceToResource(Resource res)
{
this._res = res;
res.AddReference();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (disposing)
{
this._res.RemoveReference();
}
this._disposed = true;
}
}
public Resource Resource
{
get { return this._res; }
}
~ReferenceToResource()
{
Dispose(false);
}
}
I make no claims about the suitability of this code... I just invented
it, so it may be buggy! In particular, I was undecided whether to put
the this._res.RemoveReference() within the if (disposing) or not. I'm
pretty sure it belongs there, but I'm open to being corrected.
Once you have this wrapper, you can use it like this:
using (ReferenceToResource ref = new ReferenceToResource(resource))
{
... do stuff with "resource" ...
}
The reference count will be released when leaving the scope of the
"using", whether normally or because of an exception.
You could also add the ReferenceToResource to aggregate structures if
you like, or create structures that manage the reference count
themselves, automatically.
It's not nearly as nice as C++, but fortunately the GC usually makes
this sort of thing unnecessary.