I have some questions regarding the disposable classes. Suppose I have an IDisposable implementing class having some disposable members. I have implemented the Dispose() method, i.e.:
class BaseCustom: IDisposable
{
private System.Net.Sockets.TcpClient tc;
private System.Net.Sockets.NetworkStream ns;
public string str;
public int i;
public BaseCustom(string host, int port)
{
tc = new System.Net.Sockets.TcpClient(host, port);
ns = tc.GetStream();
}
// some other methods work on members (i, str, tc, ns)
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (ns != null)
{
ns.Close();
ns = null;
}
if (tc != null)
{
tc.Close();
tc = null;
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Q1) Since there are no unmanaged resources, is it OK to surpress the finalizer? (Read note in the code here)
Q2) Since we dispose the disposable members and suppress the GC, what happens to int and string members here? Do we need to handle them also?
Q3) Is the current freeing for tc and ns proper? I see differences within .NET references across versions about calling Close() vs. Dispose()
Now suppose we have a derived class:
class DerivedCustom : BaseCustom
{
public string cstr;
public int ci;
public DerivedCustom(string host, int port)
: base(host, port)
{}
// some extra methods
}
Q4) Maybe related to Q2; do we need to override any Dispose()s here? We do not introduce any unmanaged or disposable resources in the derived class. Is it safe to leave that as is (i.e. trust the base’s dispose mechanism)? What happens to ci and cstr if the GC is suppressed (it is suppressed in the derived too, right)? Also related to Q1, do we need any finalizers?
Q5) What if the base was an abstract class?
Q6) This is interesting; the code as is does not give any warnings regarding disposability in FxCop 1.36. However if I add a disposable member to the DerivedCustom and still do not override the disposability methods (thus do not handle the new member) like:
class DerivedCustom : BaseCustom
{
public string cstr;
public int ci;
// below is the only extra line
public System.Net.Sockets.TcpClient ctc = new System.Net.Sockets.TcpClient("ho.st", 1234);
public DerivedCustom(string host, int port)
: base(host, port)
{}
// some extra methods
}
still do not get any warning in FxCop. This kinda surprises me since disposal of ctc seems to be not properly handled.
A1) If there are no unmanaged resources, don’t add a finalizer to the class. The finalizer is useful for cleaning up unmanaged resources; the GC will handle managed resources just fine by itself.
A2) SuppressFinalize doesn’t prevent the object from being garbage collected; it only stops the GC from calling the object’s finalizer. Unboxed ints are never garbage collected, as they are value types; strings will be garbage collected as usual.
A3) I would call Dispose, not Close, since Dispose is guaranteed to have Dispose semantics, while Close might do something slightly different.
A4) If the derived class doesn’t add new requirements for disposal, don’t override the Dispose method.
A5) It wouldn’t make a difference if the base was an abstract class.
A6) I don’t know enough about FXCop to answer, sorry.
Also, there’s no reason to set the fields to null. For example, in the absence of unmanaged resources, the Dispose method should look like this (EDIT: having re-read the FXCop page linked in the question, I would add that the class should be sealed in this case):
However, if the class might have a derived type that holds unmanaged resources, though, you should stick with the classic pattern, with some modifications from your example: