tags:

views:

1364

answers:

7

This class uses a StreamWriter and therefore implements IDisposable.

public class Foo : IDisposable
{
 private StreamWriter _Writer;

 public Foo (String path)
 {
  // here happens something along the lines of:
  FileStream fileWrite = File.Open (path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
  _Writer = new StreamWriter (fileWrite, new ASCIIEncoding ());
 }

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

 ~Foo()
 {
  Dispose (false);
 }

 protected virtual void Dispose (bool disposing)
 {
  if (_Disposed) {
   return;
  }
  if (disposing) {
   _Writer.Dispose ();
  }
  _Writer = null;
  _Disposed = true;
 }
 private bool _Disposed;
}

}

Is there any issue with the current implementation? I.e., do I have to release the underlying FileStream manually? Is Dispose(bool) written correctly?

A: 

If you open the StreamWriter, you have to Dispose it as well, or you'll have a leak.

Tal Pressman
Do you mean _FileStream_?
mafutrct
You probably mean FileStream, but disposing the StreamWriter does in fact also dispose the FileStream.
Martin Liversage
Thanks, too bad I can't upvote your comment as part of an answer.
mafutrct
Well, you don't have a reference to the FileStream any more... But as Martin pointed out, the StreamWriter will Dispose it for you. Besides, since the StreamWriter implements IDisposable, you have to Dispose it too.
Tal Pressman
Yes, of course, that's what the Dispose thingy is for in the OP's code.
mafutrct
An "open" StreamWriter object always has an internal FileStream in it, either if it was initialized with one you created, or initialized with a file path.
awe
+8  A: 

You don't need to use this extensive version of IDisposable implementation if your class doesn't directly use unmanaged resources.

A simple

 public virtual void Dispose()
 {

     _Writer.Dispose();
 }

will suffice.

If your consumer fails to Dispose your object it will be GC'd normally without a call to Dispose, the object held by _Writer will also be GC'd and it will have a finaliser so it still gets to clean up its unmanaged resources properly.

Edit

Having done some research on the links provided by Matt and others I've come to the conclusion that my answer here stands. Here is why:-

The premise behind the disposable implementation "pattern" (by that I mean the protected virtual Dispose(bool), SuppressFinalize etc. marlarky) on an inheritable class is that a sub-class might hold on to an unmanaged resource.

However in the real world the vast majority of us .NET developers never go anywhere near an unmanaged resource. If you had to quantify the "might" above what probabilty figure would you come up with for you sort of .NET coding?

Lets assume I have a Person type (which for sake of argument has a disposable type in one of its fields and hence ought to be disposable itself). Now I have inheritors Customer, Employee etc. Is it really reasonable for me to clutter the Person class with this "Pattern" just in case someone inherits Person and wants to hold an unmanaged resource?

Sometimes we developers can over complicate things in an attempt to code for all possible circumstances without using some common sense regarding the relative probability of such circumstances.

If we ever wanted to use an unmanaged resource directly the sensible pattern would be wrap such a thing in its own class where the full "disposable pattern" would be reasonable. Hence in the significantly large body of "normal" code we do not to have to worry about all that mucking about. If we need IDisposable we can use the simple pattern above, inheritable or not.

Phew, glad to get that off my chest. ;)

AnthonyWJones
This would only suffice if the class was sealed.
Matt Howells
@Matt: Can you elaborate?
AnthonyWJones
Posted my own answer to do so.
Matt Howells
@Anthony, its a reasonable objection, but I think more complication arises from people implementing the pattern in different, unpredictable, ways. But you seem to know what you are doing, it's your code, its up to you.
Matt Howells
@Matt: Exactly and probably in 99% of those cases no actual direct unmanaged resource was involved. Hence my advice don't do the marlarky unless you are sure you actually need to.
AnthonyWJones
Not advice I'll be following. By the way, since you haven't marked Dispose as virtual, my comment that this only suffices if the class is sealed stands.
Matt Howells
@Matt: quite right it ought to be virtual.
AnthonyWJones
I'm going to accept this answer, but I'll carefully remember Matt's considerations as well.
mafutrct
I couldn't agree more with you. The official pattern for `IDisposable` is complicated and ought to be replaced by [something better](http://codecrafter.blogspot.com/2010/01/revisiting-idisposable.html).
Jordão
Should you check if _Writer != null? I don't see anything right off that might make it null, but I usually add it anyway.
Zachary Yates
@Zachary: Yes that would be sensible pre-caution in the real world.
AnthonyWJones
+11  A: 

You don't need a Finalize (destructor) method since you don't have any unmanaged objects. However, you should keep the call to GC.SuppressFinalize in case a class inheriting from Foo has unmanaged objects, and therefore a finalizer.

If you make the class sealed, then you know that unmanaged objects are never going to enter the equation, so it isn't necessary to add the protected virtual Dispose(bool) overload, or the GC.SuppressFinalize.

Edit:

@AnthonyWJones's objection to this is that if you know that the subclasses will not reference unmanaged objects, the whole Dispose(bool) and GC.SuppressFinalize is unnecessary. But if this is the case you should really make the classes internal rather than public, and the Dispose() method should be virtual. If you know what you are doing, then feel free not to follow Microsoft's suggested pattern, but you should know and understand the rules before you break them!

Matt Howells
Doesn't that assume that the inheriting class correctly implements Dispose and hasn't just left the unmanaged clean up code in its finalizer?
AnthonyWJones
No it doesn't make any such assumption, it just permits the inheriting class to implement disposal correctly by overriding Dispose(bool).
Matt Howells
I'm sorry this just isn't making any sense to me. It seems entirely contrary to OO nature to include code in a super class just in case a sub class may do X. Surely you would leave it to the sub class to handle unmanaged resources __it uses__, finalizers and any IDisposable implementation itself. It should not assume that SuppressFinalize will be called by the base class. Why should it?
AnthonyWJones
@Anthony: Because it's part of the IDisposable.Dispose() post-conditions. The derived type is responsible for freeing both managed and unmanaged resources when Dispose(true) is called, and if it performs that responsibility (and you assume it does) then it's safe to call SuppressFinalize.
280Z28
Chris Brumme is full of win:http://blogs.msdn.com/cbrumme/archive/2004/02/20/77460.aspx
280Z28
@Anthony, I agree with you to an extent, but this is the pattern recommended by MS. I guess part of the contract you agree to when implementing IDisposable is that you will consider finalization, even of subclasses.
Matt Howells
@Matt: Can you point me in the direction of such a MS recommendation. It still looks all wrong to me. As developer of a simple inheritable class I can't see why how it can be correct to worry about an inheritor possible use of an finalizer, thats the inheritors problem not mine. Is my approach some how blocking an iheritors ability to implement a finalizer?
AnthonyWJones
Here you go: http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx
Matt Howells
Here's how I've boiled down the pattern: http://www.atalasoft.com/cs/blogs/stevehawley/archive/2006/09/21/10887.aspx
plinth
@Matt: Please see my updated answer
AnthonyWJones
+2  A: 

The recommended practice is to use a finalizer only when you have unmanaged resources (such as native file handles, memory pointers etc).

I have two small suggestions though,

It's not necessary to have the "m_Disposed" variable to test if you have previously called Dispose on your managed resources. You could use similar code like so,

protected virtual void Dispose (bool disposing)
{
    if (disposing) {
        if (_Writer != null)
            _Writer.Dispose ();
    }
    _Writer = null;
}

Open files for only as long as is necessary. So in your example, you would test for the existence of the file in the constructor using File.Exists and then when you need to read/write the file, then you would open it and use it.

Also, if you merely weant to write text to a file, have a look at File.WriteAllText or File.OpenText or even File.AppendText which is aimed at text files specifically with ASCIIEncoding.

Other than that, yes you are implementing the .NET Dispose pattern correctly.

Mike J
Regarding the second part of your post: The file has to stay open all the time. `Foo` is actually a logger. :)
mafutrct
**@mafutrct:** It is not good practice to have a file open all the time. In the function that writes to the file; open the file, write to it, and close it before function exits.
awe
+1  A: 

You should check that _Writer isn't null before attempting to dispose it. (It doesn't seem likely that it ever will be null, but just in case!)

protected virtual void Dispose(bool disposing)
{
    if (!_Disposed)
    {
        if (disposing && (_Writer != null))
        {
            _Writer.Dispose();
        }
        _Writer = null;
        _Disposed = true;
    }
}
LukeH
A: 

I agree with everything said in the other comments, but would also point out that;

  1. You don't actually need to set _Writer = null in either case.

  2. If you're going to do so, it's probably best to put it inside the if where the dispose is. It probably doesn't make a big differece, but you're generally not supposed to play with managed objects when being disposed by the finaliser (which you don't need in this case anyway, as others have pointed out).

Yort
+1  A: 

I have many classes like this - and my recommendation is to make the class sealed whenever possible. IDisposable + inheritance can work, but 99% of the times you don't need it - and it's easy to make mistakes with.

Unless you're writing a public API (in which case it's nice to permit people to implement your code however they wish to - i.e. to use IDisposable), you don't need to support it.

just do:

public sealed class Foo : IDisposable
{
    private StreamWriter _Writer;

    public Foo(string path)
    {
            FileStream fileWrite = File.Open (path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
            try { 
                _Writer = new StreamWriter (fileWrite, new ASCIIEncoding());
            } catch {
                fileWrite.Dispose();
                throw;
            }
    }

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

Note the try...catch; technically the StreamWriter constructor could throw an exception, in which case it never takes ownership of the FileStream and you must dispose it yourself.

If you're really using many IDisposables, consider putting that code in C++/CLI: that makes all that boilerplate code for you (it transparently uses the appropriate deterministic destruction tech for both native and managed objects).

Wikipedia has a decent IDisposable sample for C++ (really, if you have many IDisposable's, C++ is actually much simpler than C#): Wikipedia: C++/CLI Finalizers and automatic variables.

For example, implementing a "safe" disposable container in C++/CLI looks like...

public ref class MyDisposableContainer
{
 auto_handle<IDisposable> kidObj;
 auto_handle<IDisposable> kidObj2;
public:

 MyDisposableContainer(IDisposable^ a,IDisposable^ b) 
            : kidObj(a), kidObj2(b)
 {
  Console::WriteLine("look ma, no destructor!");
 }
};

This code will correctly dispose kidObj and kidObj2 even without adding a custom IDisposable implementation, and it's robust to exceptions in either Dispose implementation (not that those should occur, but still), and simple to maintain in the face of many more IDisposable members or native resources.

Not that I'm a huge fan of C++/CLI, but for heavily resource-oriented code it's got C# beat easily, and it has absolutely brilliant interop both with managed and native code - in short, perfect glue code ;-). I tend to write 90% of my code in C#, but use C++/CLI for all interop needs (esp. if you want to call any win32 function - MarshalAs and other interop attributes all over the place are horrifying and thoroughly incomprehensible).

Eamon Nerbonne