views:

514

answers:

4

Say I want to define a TempFileStream class that creates a temporary file using Path.GetTempFileName() method. A temporary file must be deleted when TempFileStream's object is no longer needed, e.g. closed or disposed:

class TempFileStream: FileStream
{
  string m_TempFileName = Path.GetTempFileName();
  public TempFileStream(FileMode fileMode): base(m_TempFileName,fileMode) {}

  /// ...

 public ovverride Dispose(bool disposing)
 {
   /// ???
 }

}

How should I implement this simply and safely?

+1  A: 
base.Dispose(disposing); // disposes the base stream so the file is no longer used
if (disposing)
    File.Delete(m_TempFileName); // deletes the file

You should add proper exception handling for File.Delete if you need to.

Julien Lebosquain
A: 

Basically according to TempFileStream logic you always use just created file with unique name (that is what Path.GetTempFileName does) and you always delete it after its use. So there is no need to provide constructor that accepts FileMode as you always use it in the same mode.

Dzmitry Huba
Good point. :) Thanks.
sh0gged
+1  A: 

This is an interesting idea, but there's something about this design that troubles me. Forgive me if you've already addressed this in your design. But if your design is just a simple wrapper around FileStream, there's a subtle but, I think, significant problem.

If you're deleting the file when the stream is closed, that means that the only way to actually use the data in the file is if the FileAccess is ReadWrite. Correct? In other words, you'll be using the file with code that looks like this:

using (TempFileStream t as new TempFileStream())
{
   WriteDataToTempFile(t);
   t.Seek(0, SeekOrigin.Begin);
   ReadDataFromTempFile(t);
}

The problem I see is that ReadDataFromTempFile is expecting the file to be opened for read access, not read/write access. And this opens the door for some bugs that I think will be very hard to find. Consider code like this:

using (TempFileStream t as new TempFileStream())
{
   MyClass o = new MyClass(o);
   o.TempStream = t;
   o.ProduceOutput();
   t.Seek(0, SeekOrigin.Begin);
   o.ProcessOutput();
}

...when compared with this:

MyClass o = new MyClass();
string n = Path.GetTempFileName();
using (FileStream s = new FileStream(n, FileMode.Create, FileAccess.Write))
{
   o.TempStream = t;
   o.ProduceOutput();
}
using (FileStream s = new FileStream(n, FileMode.Open, FileAccess.Read))
{
   o.TempStream = t;
   o.ProcessOutput();
}
File.Delete(n);

Sure, the first method is shorter than the second. But the second method will throw an exception if ProcessOutput calls a method that writes to TempStream. (Or sets a property whose set accessor raises an event whose event handler dispatches a call to a method that writes to TempStream, which how this problem will probably end up happening.) The first one will just produce unexpected results for no apparent reason.

You can get around this, I think, by having your TempFileStream class open the underlying FileStream using FileAccess.Write. Then implement a Rewind method that closes this FileStream and creates a new one that uses FileAccess.Read. If you do that, any method that tries to write to the file while it's opened for read access (or vice versa) will at least throw an exception.

Robert Rossney
Good point, thanks a lot. I should take this into account. Initially I thought about just a simple wrapper.
sh0gged
+3  A: 

Try this one instead:

public class TempFileStream : FileStream
{
    public TempFileStream()
        : base(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose) { }
    public TempFileStream(FileAccess access)
        : base(Path.GetTempFileName(), FileMode.Create, access, FileShare.Read, 4096, FileOptions.DeleteOnClose) { }
    public TempFileStream(FileAccess access, FileShare share)
        : base(Path.GetTempFileName(), FileMode.Create, access, share, 4096, FileOptions.DeleteOnClose) { }
    public TempFileStream(FileAccess access, FileShare share, int bufferSize)
        : base(Path.GetTempFileName(), FileMode.Create, access, share, bufferSize, FileOptions.DeleteOnClose) { }
}

The FileOptions.DeleteOnClose option will ensure that the OS deletes the temporary file automatically when you close out the file. No need for a special Dispose method, because it's all taken care of for you.

Extremeswank
+1 for DeleteOnClose, didn't know that one, comes handy!
axel_c
+1, I love how much I learn just reading other people's answers on this site.
shambulator