tags:

views:

83

answers:

4

This seems like a fairly straightforward question, but I couldn't find this particular use-case after some searching around.

Suppose I have a simple method that, say, determines if a file is opened by some process. I can do this (not 100% correctly, but fairly well) with this:

public bool IsOpen(string fileName)
{
  try
  {
    File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None);
  }
  catch
  {
    // if an exception is thrown, the file must be opened by some other process
    return true;
  } 
}

(obviously this isn't the best or even correct way to determine this - File.Open throws a number of different exceptions, all with different meanings, but it works for this example)

Now the File.Open call returns a FileStream, and FileStream implements IDisposable. Normally we'd want to wrap the usage of any FileStream instantiations in a using block to make sure they're disposed of properly. But what happens in the case where we don't actually assign the return value to anything? Is it still necessary to dispose of the FileStream, like so:

try
{
  using (File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None));
  { /* nop */ }
}
catch 
{
  return true;
}

Should I create a FileStream instance and dispose of that?

try
{
  using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None));
}
...

Or are these totally unnecessary? Can we simply call File.Open and not assign it to anything (first code example), and let the GC dispose of it right away?

+2  A: 
stakx
+10  A: 

Yes, you should definitely dispose of the FileStream. Otherwise the stream will remain open and the file won't be usable until a finalizer happens to clean it up.

The important thing here is ownership: for File.Open, the caller is assumed to "own" the stream returned to it - and if you own something which implements IDisposable, it's your responsibility to dispose of it.

Compare this with the situation of Image.FromStream: in that case, you pass in a stream and the Image then assumes that it owns that stream. You mustn't close the stream yourself, in that case - you have to dispose of the image when you're done, and it will dispose of the stream.

Calling a static method which returns something disposable almost always assumes that the caller takes ownership of the resource. Ditto constructors (which are effectively static methods.)

The irony in this case is that if you don't dispose of the stream returned by File.Open, you'll have found that the file is usable - at the same time as making it unusable until some indeterminate time.

Jon Skeet
Perfect, thanks! Again, this is all fake code, I just needed an example of a method that would return an IDisposable - I should have named all the methods Foo.ThisReturnsAnIDisposable to pre-empt the "dont do this with files!" comments :)
mjd79
A: 

When you call any method that returns something, an instance of that something is being created. Just because you're not actually capturing it, doesn't make it any less "there". Therefore, in the case of an IDisposable object, the object is being created by the method you're calling in spite of the fact that you're doing nothing with it. So yes, you still need to dispose of it somehow. The first approach with the using statement seems like it should work.

BFree
+1  A: 

Yes, you don't want to leave the FileStream opened. For one, you won't even be able to open the file yourself after that. Calling Close() is good enough, but using using is probably the preferred pattern.

There's a much bigger problem with your code however. It cannot possibly work reliably on Windows. A typical scenario:

  • The File.Open() call succeeds. You close it
  • Your thread gets pre-empted by the Windows scheduler
  • Another thread in another process gets a chance to run, it opens the file
  • Your thread regains the CPU and continues after the File.Open() call
  • You open the file, trusting that it will work since IsOpen() returned false.

Kaboom.

Never write code like this, failure is extremely hard to diagnose. Only ever open a file when you are ready to start reading or writing to it. And don't close it until you are done.

Extra bonus: it is now obvious that you want to use a using statement.

Hans Passant
Right - again, the code is all fake. I wouldn't do this with actual production code. I just needed a quick and dirty example of something that returned an IDisposable, and File.Open happened to be on my screen last :)
mjd79