Hi all, I was pondering on some of the issues re. garbage collection, ref
counting and deterministic finalization which have been posted here and had
an idea, so I though I'd post it here and see what people think.
Note - this idea may well be way off base. Anyway...
It occurred to me that one of the main problems with IDisposable is the
lack of guarantee that finalization will take place as there is virtually
no way to even warn the programmer that they are misusing a class derived
from IDisposable - e.g. the programmer is not ensuring that a resource is
disposed within a try/finally block, a using clause or that in the case of
a class which stores a reference as a member variable that the class itself
implements IDisposable.
Now, it occurred to me that one way of ensuring that a disposable resource
is disposed of correctly would be to enforce an ownership rule, whereby a
disposable resource MUST always have an assigned owner.
This assigned owner could be a class instance, but could also be a scope
block, local function scope, or assembly scope.
In my example, this would involve the specification of two interfaces - one
for a disposable resource class and one for a disposable resource class
owner. See below.
interface IDisposable {
void AssignOwner(IDisposableOwner owner);
void Dispose();
}
interface IDisposableOwner : IDisposable {
void AddResource(IDisposable resource);
void RemoveResource(IDisposable resource);
}
The implementation of the two interfaces would look something like the
following examples:
class MyResource : IDisposable
{
IDisposableOwner m_owner = null;
File m_file = new File("c:\temp.file");
public void AssignOwner(IDisposableOwner owner)
{
if(m_owner != null) m_owner.RemoveResource(this);
owner.AddResource(this);
m_owner = owner;
}
public void Dispose()
{
m_file.close();
if(m_owner != null) m_owner.RemoveResource(this);
}
}
class MyResourceOwner : IDisposableOwner
{
ArrayList m_resources = new ArrayList();
IDisposableOwner m_owner = null;
public void AddResource(IDisposable resource)
{
m_resources.Add(resource);
}
public void RemoveResource(IDisposable resource)
{
m_resources.Remove(resource);
}
public void AssignOwner(IDisposableOwner owner)
{
if(m_owner != null) m_owner.RemoveResource(this);
owner.AddResource(this);
m_owner = owner;
}
public void Dispose()
{
// dispose of each resource - this will result in a call to
// RemoveResource, so the size of our list should decrease by
// one for each call until we have no resources left.
while(m_resources.Count() > 0) m_resources[0].Dispose();
}
}
I know that the above code does not handle all contingencies such as
multiple calls to dispose etc. but this is more for example.
Note that most of the above code could be auto-generated by the compiler so
that the above code could probably be replaced with something like the
following:
[disposable()]
class MyResource
{
File m_file = new File("c:\temp.file");
void Dispose()
{
m_file.close();
}
}
[disposableowner()]
class MyResourceOwner
{
}
Now when instantiating a disposable class, the compiler would require the
specification of an owner. The specification of the owner might be
specified through a modified new operator for disposable resources, e.g.
new(owner) ClassName(parameters...)
The new operator would behind the scenes call AssignOwner(owner) on the
created disposable class.
We now also add some new keywords, which allow the use of scope to define
an instance of a scope class which implements IDisposableOwner and handles
release of resources owned by a scope. The scope instance would be
disposed of at the end of the code block which it encloses.
This could look something like this:
scope(scope name)
{
}
We would have some predefined instances of scope also for local function
scope and for the assembly (in the case of assembly, when the assembly is
unloaded, it would release all disposable resources it owns).
Some examples then of how this would look. Note, this example would make
use of the previously defines MyResource class.
[disposableowner()]
class MyExample
{
MyResource m_resource;
public MyExample()
{
// create an instance of the resource with ourselves as
// the owner
m_resource = new(this) MyResource();
}
public void Example1()
{
// create an instance local to the function, using the predefined
// localscope instance (note compiler would only create instance
// if used).
MyResource inst_localToFunc = new(localscope) MyResource();
scope(myScope)
{
// create an instance local to this scope
MyResource inst_localToScope = new(myScope) MyResource();
} // here Dispose is called on the myScope scope instance
}
public MyResource CreateResource(IDisposableOwner owner)
{
return new(owner) MyResource();
}
public void StoreResource(MyResource resource)
{
// assign ourself as the new owner of this resource
resource.AssignOwner(this);
// release the current resource if any
if(m_resource != null) m_resource.Dispose();
// assign reference to member variable for use later by this class
m_resource = resource;
}
}
I can see a couple of things which this doesn't account for. For example,
in StoreResource, the developer still needs to take care to dispose of the
current resource prior to assignment to m_resource. If this was not done,
the resource would still be released upon disposal of the MyExample
instance, but obviously it should be done earlier.
So, any opinions on this ? There are probably glaring errors, but I
thought I'd post and see what anyone thinks.
Phil.
You can read messages from the Advanced DOTNET archive, unsubscribe from Advanced
DOTNET, or
subscribe to other DevelopMentor lists at http://discuss.develop.com.