Jump to content
  • Advertisement
Sign in to follow this  
Orcy

[.net] Implementing Dispose()

This topic is 4578 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have read a lot stuf of how to implement Dispose(). But there is one thing, that I cannot understand. I have a class A. Class B and C contain the same object of this class (they are not inherit from A). Class A implements the Dispose() method. So i have to implement it in class B and C, too, to give the developer the ability to dispose the helded resources. Now my question: When I dispose B, A will be disposed, too. But C is still holding a reference to A, which is now disposed. What am I doing wrong? here some code, it is just a sample of that what i mean. ///////////////////////////////////////////////////// using System; using someOtherAssembly; static class Program { static void Main() { A parent = new A(); B child1 = new B(parent); C child2 = new C(parent); child1.Dispose(); // now child2 cannot be used anymore, because 'parent' was disposed, too. } } class A : IDisposable { bool isDisposed; IntPtr someUnmanagedResource; public A() { CreateUnmanagedResource(someUnmanagedResource); } ~A() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool isDisposing) { if (isDisposed) return; if (isDisposing) { // no managed resources to dispose, so do nothing } FreeUnmanagedResource(someUnmanagedResource); } } class B : IDisposable { bool isDisposed; A objectA; public B(A parent) { objectA = A; } ~B() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool isDisposing) { if (isDisposed) return; if (isDisposing) { objectA.Dispose(); } } } class C : IDisposable { bool isDisposed; A objectA; public C(A parent) { objectA = A; } ~C() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected void Dispose(bool isDisposing) { if (isDisposed) return; if (isDisposing) { objectA.Dispose(); } } } ///////////////////////////////////////////////////// When I would create a copy there wouldn't be the problem anymore. But in my real project, class A hold very much more data, a copy would cost too much memory. Thanks in advance.

Share this post


Link to post
Share on other sites
Advertisement
Technically B still holds a reference to A, you might want to add a "A = null" in the dispose. I really dont see a problem here, the reference to A in C after B is disposed is still valid.

Dispose != finalize, finalization will only happen after all references are gone, supressing finalization simply tells the GC that yes, there is unmanaged data but I have already take care of it so you dont have to call finalize ( or the deconstructor ) which adds some overhead in the GC when actually destroying an object. Try putting some debug output in both the dispose and finalize functions of all your classes to see the order things happen.

Share this post


Link to post
Share on other sites
If ClassA holds some unmanaged resource that can infact be shared across multiple managed objects, then you should implement some kind of reference counting mechanism. That way when a client of A disposes itself, A will only be disposed when A the client is the last object referencing A.


public class A
{
protected int m_refCount;
// Other code...

private static globalA; // Too expensive to allow many different copies.
public static A GetAnAObject()
{
if (null == globalA)
(
globalA = new A(/*Whatever constructor*/);
}

// We could put in some logic to either create a new object,
// or return an existing one based on the reference count.


globalA.m_refCount++ // increase the reference count.
return globalA
}

public void Dispose()
{
// Decrement the reference count.
if (--m_refCount <= 0)
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}


All in all it sounds like you just need to employ a couple of simple design patterns.

0. You'll probably need to add a reference counting mechanism for class A. These can be internal to A, or external (via some Factory cleanup method.) But you'll need some mechanism to keep track how many clients have a reference to any one A object. If you only want one A object at any time, then you should look at making A a Singleton.

1. Factory: If A really is expensive to create and copy, but you can have more than one A object in existance, you may want to use an Interface (i.e. interface IA) and Factory pattern to keep a client from just creating A whenever they please. B and C would now reference the interface IA instead of the A object. You could be extra Ninja and make the A class a member class of the AFactory object, so there's no way to create an A object except for when the AFactory class allows it through its public method.

2. If it's just more convenient to be able to share a reference to A for performance reasons (like a Socket or Database Connection) then a Disposable object that has a 'smart' Dispose method like the one I've described will work quite well.

I'm not sure exactly what your constraints are, but I think the above ideas should point you in the right direction. links to the various patterns:

Factory Pattern
Singleton Pattern
Observer Pattern (you might want to look into this one...)
Reference Counted Object (or Smart Pointer)

should be available on Wikipedia or in the Addison Wesley Books
Design Patterns by Gamma et al,
Design Patterns in C# by Steven John & John Metsker

Good luck!

Share this post


Link to post
Share on other sites
A great "developer-friendly" way of implementing this is having a DestructableObject class which implements all that's needed for Dispose along with a CloseHandle method to close anything that really needs closing:


public abstract class DestructableObject : IDisposable
{
private bool m_Disposed = false;
private IntPtr m_Handle = IntPtr.Zero;

protected DestructableObject(IntPtr handle)
{
m_Handle = handle;
}

protected DestructableObject()
{
}

~DestructableObject()
{
Dispose(false);
}

protected internal IntPtr Handle
{
get
{
return m_Handle;
}
set
{
m_Handle = value;
GC.KeepAlive(this);
}
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (!m_Disposed)
{
if (disposing)
{
// dispose any managed resources
}
CloseHandle(); // dispose any unmanaged resources (derived)
m_Handle = IntPtr.Zero;
}
m_Disposed = true;
}

protected abstract void CloseHandle();
}



...And then when you're looking to implement a class which will be holding unmanaged resources, you just derive from DestructableObject and implement CloseHandle in which you'll call whatever function you need to destroy the handle. Good luck!

Share this post


Link to post
Share on other sites
I think however that the bigger question is whether each class that holds a reference to "Class A" should be responsible for cleaning that up. If your class is truly holding something that *must* be disposed of (usually unmanaged resources), then the code that instantiated that class should be responsible for disposing of it.

compare this to how you would handle a database connection. You might pass the SqlConnection into a bunch of other methods, or set it as a property of another class, but 9 times out of 10, the code that instantiated the class will be the one that calls the Close() method.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rob Loach
A great "developer-friendly" way of implementing this is having a DestructableObject class which implements all that's needed for Dispose along with a CloseHandle method to close anything that really needs closing:

*** Source Snippet Removed ***

...And then when you're looking to implement a class which will be holding unmanaged resources, you just derive from DestructableObject and implement CloseHandle in which you'll call whatever function you need to destroy the handle. Good luck!
Or use SafeHandle if you're on .NET 2.0.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!