I'll try to answer my own question:
Avoid It in the First Place
The easiest way out of this situation is to refactor the code to avoid the problem entirely.
There are two obvious ways to do this.
External instance creation
If AContainer
does not create a SomeDisposableObject
instance, but instead relies on external code to supply it, then AContainer
will no longer "own" the instance and is not responsible for disposing of it.
The externally created instance could be supplied via the constuctor or by setting the property.
public class AContainerClass
{
SomeDisposableObject m_someObject; // No creation here.
public AContainerClass(SomeDisposableObject someObject)
{
m_someObject = someObject;
}
public SomeDisposableObject SomeObject
{
get { return m_someObject; }
set { m_someObject = value; }
}
}
Keep the instance private
The main issue with the posted code is that the ownership is confused. At Dispose time the AContainer
class cannot tell who owns the instance. It could be the instance that it created or it could be some other instance that was created externally and set
via the property.
Even if it tracks this and knows for certain that it is dealing with the instance that it created, then it still cannot safely dispose of it as other classes may now have a reference to it that they obtained from the public property.
If the code can be refactored to avoid making the instance public (i.e. by removing the property entirely) then the issue goes away.
And If It Can't Be Avoided...
If for some reason the code cannot be refactored in these ways (as I stipulated in the question) then in my opinion you are left with some fairly difficult design choices.
Always Dispose of the instance
If you choose this approach then you are effectively declaring that AContainer
will take ownership of the SomeDisposableObject
instance when the property is set.
This makes sense in some situations, particularly where SomeDisposableObject
is clearly a transient or subservient object. However it should be documented carefully as it requires the calling code to be aware of this transfer of ownership.
(It may be more appropriate to use a method, rather than a property, as the method name can be used to give a further hint about ownership).
public class AContainerClass: IDisposable
{
SomeDisposableObject m_someObject = new SomeDisposableObject();
public SomeDisposableObject SomeObject
{
get { return m_someObject; }
set
{
if (m_someObject != null && m_someObject != value)
m_someObject.Dispose();
m_someObject = value;
}
}
public void Dispose()
{
if (m_someObject != null)
m_someObject.Dispose();
GC.SuppressFinalize(this);
}
}
Only Dispose if still the original instance
In this approach you would track whether the instance was changed from the one originally created by AContainer
and only dispose of it when it was the original. Here the ownership model is mixed. AContainer
remains the owner of its own SomeDisposableObject
instance, but if an external instance is supplied then it remains the responsibility of the external code to dispose of it.
This approach best reflects the actual situation here, but it can be difficult to implement correctly. The client code can still cause issues by performing operations like this:
AContainerClass aContainer = new AContainerClass();
SomeDisposableObject originalInstance = aContainer.SomeObject;
aContainer.SomeObject = new SomeDisposableObject();
aContainer.DoSomething();
aContainer.SomeObject = originalInstance;
Here a new instance was swapped in, a method called, then the original instance was restored. Unfortunately the AContainer
will have called Dispose()
on the original instance when it was replaced, so it is now invalid.
Just give Up and let the GC handle it
This is obviously less than ideal. If the SomeDisposableObject
class really does contain some scarce resource then not disposing of it promptly will definitely cause you issues.
However it may also represent the most robust approach in terms of how the client code interacts with AContainer
as it requires no special knowledge of how AContainer
treats the ownership of the SomeDisposableObject
instance.
If you know that the disposable resource isn't actually scarce on your system then this may actually be the best approach.
Some commenters have suggested that it may be possible to use reference counting to track if any other classes still have a reference to the SomeDisposableObject
instance. This would be very useful as it would allow us to dispose of it only when we know it is safe to do so and otherwise just let the GC handle it.
However I am not aware of any C#/.NET API for determining the reference count of an object. If there is one then please let me know.