views:

242

answers:

9

This is a rather basic question, however I'm still struggling with it a little.

IDisposable is implemented, when you want to enable the user of an object to free underlying resources (e.g. sockets etc.) before the object is eventually garbage collected.

When i have a class that holds a DbConnection (implements IDisposable), does my class need to implement IDisposable too and chain the call down to the DbConnection or any other IDisposable objects it owns? Else the DbConnections resources will only be freed when my class is GarbageCollected, thereby drops it reference to the connection and GC will finalize the DbConnection.

+6  A: 

Yes, your class needs to be IDisposable if it needs to dispose of any objects that it uses. An example of this is StreamReader. It implements IDisposable so it can dispose of its associated stream object.

David
question is if it _does_ need to dispose any objects. It _could_. How do you think about the DbConnection example.
Johannes Rudolph
@Johannes: An example of this is your class. It should implement IDisposable so it can dispose of its associated DbConnnection object.
R. Bemrose
(Yes, I intentionally paraphrased what David said)
R. Bemrose
A: 

It is certainly best practice to do so, especially when working with heavy/unmanaged objects.

Edit: Best practice, but not mandatory.

jenningj
+3  A: 

If I understand your question correctly, you have a class that uses a DbConnection. You want to ensure that the DbConnection is correctly disposed when you are done working with it or when your class is disposed. There are a couple of ways to achieve this.

If you are using a database connection as a local variable in a method then you can leverage the using() {} statement.

using (SqlConnection sqlConnection = new SqlConnection(connStr))
{
...do stuff with connection here
}

The using () {} statement automatically calls Dispose() on objects declared within the (). (It also requires that objects declared in the () implement IDisposable to ensure they can be disposed)

If you are instead working with a DbConnection as a private variable that is initialized during object construction or some other initialization method then you probably want to implement IDisposable yourself and then call _dbConnection.Dispose() in your Dispose() method. That way, when your object is disposed the db connection object will also dispose.

public class MyDALObj : IDisposable
{

public MyDalObj()
{
... create _dbConn object ...
}

public void Dispose()
{
_dbConn.Dispose();
}

private DbConnection _dbConn;
}

Stuart Thompson
+2  A: 

You should do this, since it's the only way for the user of your class to make sure that the internally held resource is properly disposed.

However, the pattern used for Dispose() may be slightly different than what is commonly written, in this case, since you don't have to differentiate between unmanaged and managed resources (your encapsulated resource is always treated as a "managed" resource).

I wrote a detailed blog post on this specific topic - Encapsulating IDisposable Resources.

Reed Copsey
A: 

Since we never know when an object is going to be collected by GC we use IDisposable interface to have a chance to intentionally release unmanaged resources before an object is garbage collected. If an disposable object is not dispose before being collected its resources might not been released until the AppDomain is exited. It's almost an unwritten rule that every object that has a reference to an IDisposable object should be IDisposable itself and call Dispose method of its IDisposable references in its own Dispose method.

Beatles1692
A: 

Of course you can remove a lot of the (re)implementation cost of IDisposable and get something pretty close to deterministic finalization of objects on the managed heap if you use C++/CLI. This is a often (I find) overlooked aspect of a language that a lot of folks seem to consign to the "for glue code only" bin.

Tom Kirby-Green
A: 

When you provide explicit control using Dispose, you should provide implicit cleanup using the Finalize method. Finalize provides a backup to prevent resources from permanently leaking if the programmer fails to call Dispose.

I think best way to implement this is use combination of Dispose and Finalize method. You can find more Here.

Neil
Finalizers give you a last-second chance to free up your OWN unmanaged resources if the Dispose() method wasn't called. You only implement a finalizer if you have unmanaged resources to clean up.The finalizer doesn't call member objects' Dispose() methods. Rather, your dispose method should call your finalizer instead (and then surpress finalization).
tylerl
exactly, because references to other objects are not guaranteed to be valid in the finalizer. They might have been collected before.
Johannes Rudolph
A: 

There are two distinct scenarios:

  1. Your object is given an object reference to use, either through a constructor argument or a property, and this object implements IDisposable.
  2. Your object constructs an instance of an object that implements IDisposable.

In the second case, your object is responsible for the resources involved, so your object must implement IDisposable, and when disposed of, you should dispose of the object you constructed.

Your DbConnection falls under this second case, so yes, your object should implement IDisposable, and then dispose of the connection.

In the first case, you need to decide on the following three solutions:

  1. Your object is only referencing an external object. Your object should not dispose of this external object. You do not need to implement IDisposable for this case (that is, for this specific object, if you also internally constructs a disposable object, you're back to the second case above).
  2. Your object is taking responsibility for the external object. In this case, you're back to the second case, even though your object was not the one constructing this external object. Here you implement IDisposable, and dispose of the object you're given.
  3. You implement a way for the outside world to tell you which of the first two solutions to pick. For instance, a constructor might be given a connection, and a boolean argument (or ideally an enum value) that tells the constructor whether your object now owns the supplied connection. Here you also need to implement IDisposable, but in the Dispose method, you need to check the ownership and only dispose of the supplied connection if you own it.

That was a lot of text, so let me summarize:

  1. Objects you own, you need to dispose of.
  2. Objects you don't, you do not dispose.

There's also a third case, which it doesn't sound like you're having, but nonetheless.

In the case when you construct, use, and discard, an object locally, inside a single method, without passing it around or storing it in fields of the class, you use the using statement instead, like this:

using (IDbConnection conn = ....())
{
}
Lasse V. Karlsen
+4  A: 

Yes, you ALWAYS implement IDisposable if you control disposable objects. ALWAYS. Your code won't break if you don't, but it defeats the purpose of having disposable objects if you don't.

The general rule for GC optimization is:

  • Any class that controls objects not managed by the GC must implement a finalizer (and generally should implement IDisposable as well). This is where the "top level" disposable classes usually come from -- they usually control a HANDLE to a window, socket, mutex, or what have you.
  • Any class that instantiates an IDisposable member should implement IDisposable itself, and properly Dispose() of its constituents.
  • Any function that instantiates an IDisposeable object should properly Dispose() of it when it done being used. Don't let it just drop out of scope.

These rules may be bent or ignored if you're writing an application for yourself, but when distributing code to others you should be professional and follow the rules.

The logic here is that when you control memory outside the view of the GC the GC engine can't properly manage your memory usage. On your .NET heap, for example, you may just have a 4-byte pointer, but out in unmanaged land you could have 200 MB of memory being pointed at. The GC engine wouldn't attempt to collect these until you have several dozen, because all it sees is a few bytes; while in the real world it looks a lot like a memory leak.

Therefore, the rule is, unmanaged memory should get freed immediately when you're done using it (the IDisposable chain does this for you), while managed memory gets freed by the GC engine whenever it gets around to it.

tylerl
Isn't it curious then that DataSet.Dispose() doesn't take care to Dispose() its DataTables?
Nariman