6

MSDN tells us that when you call "File.Delete( path );" on a file that doesn't exist an exception is generated.

Would it be more efficient to call the delete method and use a try/catch block to avoid the error or validate the existence of the file before doing the delete?

I'm inclined to think it's better to avoid the try/catch block. Why let an error occur when you know how to check for it.

Anyway, here is some sample code:

// Option 1: Just delete the file and ignore any exceptions

/// <summary>
/// Remove the files from the local server if the DeleteAfterTransfer flag has been set
/// </summary>
/// <param name="FilesToSend">a list of full file paths to be removed from the local server</param>
private void RemoveLocalFiles(List<string> LocalFiles)
{
    // Ensure there is something to process
    if (LocalFiles != null && LocalFiles.Count > 0 && m_DeleteAfterTransfer == true)
    {
        foreach (string file in LocalFiles)
        {
            try { File.Delete(file); }
            catch { }
        }
    }
}

// Option 2: Check for the existence of the file before delting
private void RemoveLocalFiles(List<string> LocalFiles )
{
    // Ensure there is something to process
    if (LocalFiles != null && LocalFiles.Count > 0 && m_DeleteAfterTransfer == true)
    {
        foreach (string file in LocalFiles)
        {
            if( File.Exists( file ) == true)
                File.Delete(file);
        }
    }
}

Some Background to what I'm trying to achieve: The code is part of an FTP wrapper class which will simplify the features of FTP functionality to only what is required and can be called by a single method call. In This case, we have a flag called "DeleteAfterTransfer" and if set to true will do the job. If the file didn't exists in the first place, I'd expect to have had an exception before getting to this point. I think I'm answering my own question here but checking the existence of the file is less important than validating I have permissions to perform the task or any of the other potential errors.

TeamWild
  • 2,460
  • 8
  • 43
  • 53
  • Abel - a comment saying 'pick my answer' ? :) – Kieren Johnstone Aug 12 '11 at 08:27
  • @Kieren: haha, no, you're right. I'll redo: Suggestion: use [`FileInfo.Delete`](http://msdn.microsoft.com/en-us/library/system.io.fileinfo.delete.aspx), it doesn't raise an exception when the file isn't there. – Abel Aug 12 '11 at 08:31
  • @TeamWild: another small optimization to your code: the `LocalFiles.Count > 0` is not necessary. The `foreach` won't loop when the count is zero. Also note that Kieren posted some interesting side scenario's that might happen under my post, perhaps it's applicable. – Abel Aug 12 '11 at 08:55
  • 1
    Actually [MSDN](http://msdn.microsoft.com/en-us/library/system.io.file.delete(v=VS.80).aspx) says that no exception is generated ! – Alexandre C. Aug 12 '11 at 08:56
  • @Alexandre C. I'm using .Net 3.5 so there is an exception. – TeamWild Aug 12 '11 at 09:01
  • 1
    [MSDN](http://msdn.microsoft.com/en-us/library/system.io.file.delete(v=VS.90).aspx) says that no exception is generated either for 2.0, 3.0, 3.5, or 4.0. – Alexandre C. Aug 12 '11 at 09:01
  • @TeamWild: you are mentioning "an exception" perhaps we're misinterpreting it. Because you are catching _all_ exceptions (never do that!) it's perhaps unclear what the exception is. Is there a specific exception thrown by `File.Delete`? – Abel Aug 12 '11 at 10:38
  • @Alexandre C. Sorry. I'd mis-read the text. – TeamWild Aug 12 '11 at 12:50

8 Answers8

7

You have essentially three options, considering that File.Delete does not throw an exception when your file isn't there:

  • Use File.Exists, which requires an extra roundtrip to the disk each time (credits to Alexandre C), plus a roundtrip to the disk for File.Delete. This is slow. But if you want to do something specific when the file doesn't exist, this is the only way.

  • Use exception handling. Considering that entering a try/catch block is relatively fast (about 4-6 m-ops, I believe), the overhead is negligible and you have the option to catch the specific exceptions, like IOException when the file is in use. This can be very beneficial, but you will not be able to act when the file does not exist, because that doesn't throw. Note: this is the easiest way to avoid race conditions, as Alexandre C explains below in more detail.

  • Use both exception handling and File.Exists. This is potentially slowest, but only marginally so and the only way to both catch exceptions and do something specific (issue a warning?) when the file doesn't exist.


A summary of my original answer, giving some more general advice on using and handling exceptions:

  • Do not use exception handling when control flow suffices, that's simply more efficient and more readable.
  • Use exceptions and exception-handling for exceptional cases only.
  • Exception handling entering try/catch is very efficient, but when an exception is thrown, this costs relatively much.
  • An exception to the above is: whenever dealing with file functions, use exception handling. The reason is that race conditions may happen and that you never know what occurs between your if-statement and your file-delete statement.
  • Never ever, and I mean: never ever use a try/catch for all exceptions (empty catch block, this is almost always a weak point in your application and needs improvement. Only catch specific exceptions. (exception: when dealing with COM exceptions not inheriting from Exception).
Community
  • 1
  • 1
Abel
  • 56,041
  • 24
  • 146
  • 247
  • So long as `FileInfo.Delete` still throws an exception if there is a problem deleting the file? – Kieren Johnstone Aug 12 '11 at 08:25
  • @Kieren: that's the kind of exception that's actually an exceptional situation, which you do want to treat as an exception. But these you would have to deal with in any file-access scenario. – Abel Aug 12 '11 at 08:35
  • Yes I'm saying that so long as it does still throw an exception if there's a problem, that's OK - if it hides problems and doesn't throw, it may not be suitable.. ? The complexities of if the folder doesn't exist, or the network share isn't available - what are the results there? Do we want it to fail silently? etc. – Kieren Johnstone Aug 12 '11 at 08:39
  • @Kieren: ah, I see. You mean that if you actually want to do _something_ when the file isn't there as opposed to ignoring it. Indeed, I agree, then you should use `File.Exists` or catching exceptions (whichever suits the particular scenario). Do note though that FileInfo.Delete _does_ throw exceptions, just none when the file doesn't exist. – Abel Aug 12 '11 at 08:42
  • What about when the folder containing it doesn't exist? Should that be an exception case or not? – Kieren Johnstone Aug 12 '11 at 08:50
  • @Kieren: I don't know, that's up to the OP. But that's still an improvement over his original code, where he clearly ignores the existence of the file and doesn't check the path before doing so. – Abel Aug 12 '11 at 08:53
  • Here catching would have been better because the file could have been deleted/created between calls to `File.Exists` and `File.Delete`. But I agree with the general idea. – Alexandre C. Aug 12 '11 at 09:25
  • "One can think of the difference in performance between a test File.Exists and an exception that needs unwinding, the latter being slower" Actually calling `File.Exists` will cause an extra roundtrip to the disk and might actually be *slower* than unwinding an exception (even if exception performance truly suck in .NET) *[I'm not the downvoter, just commenting on that sentence]* – Alexandre C. Aug 12 '11 at 12:48
  • @Alex: I see your point and I think you're right, I will update my question with that in mind. – Abel Aug 12 '11 at 15:57
  • I downvoted this even though it's two years afterwards. Any approach other than Andre C's below risks race conditions. – Jim Holmes Sep 30 '13 at 11:07
  • @JimHolmes: (I assume you mean _Alexandre C_), no, there are other ways to avoid race conditions, but they are more involved. Also, my second bullet point is equal to Alexandre's. I've updated the answer to reflect the fact that it avoids race conditions. – Abel Oct 01 '13 at 12:34
4

Another option: use Windows API DeleteFile...

[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool DeleteFile(string path);

This returns true if done, otherwise false. If false, you don't have the large overhead of Exceptions.

robertpnl
  • 694
  • 2
  • 10
  • 24
4

MSDN says that no exception is generated. Actually, it is better this way since you have a race condition: between calls to File.Exists and File.Delete the file could have been deleted or created by another process.

If it were to throw, you are better catching the specific exception that could have been thrown (FileNotFoundException or similar). Note that because of the race condition, exceptions are the only way to go here.

If your problem is that the directory containing the file doesn't exist, then you can do:

if (LocalFiles != null && m_DeleteAfterTransfer == true)
{
    foreach (string file in LocalFiles)
    {
        try { File.Delete(file); }
        catch (DirectoryNotFoundException e) {}
    }
}

Again, don't check for the directory existence before, because 1) it is cumbersome 2) it has the same race condition problem. Only File.Delete guarantees that the check and the delete are atomically executed.

Anyways you never want to catch every exception here since file IO methods can fail for a lot of reasons (and you surely don't want to silent a disk failure !)

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
2

There are many exceptions that can occur when trying to delete a file. Take a look here for all of them. So it's probably best to catch and deal with each of them as you see fit.

  • 1
    I should also have read further - take a look at the remarks section of that page, it states "If the file to be deleted does not exist, no exception is thrown." – Peter vd Merwe Aug 12 '11 at 08:18
1

It's far better to do File.Exists first. Exceptions have a large overhead. In terms of efficiency, your definition isn't very clear, but performance and memory-wise, go for File.Exists.

See also my previous answer in this question about using Exceptions to control program flow:

Example of "using exceptions to control flow"

As below, I welcome anyone to try this themselves. People talking about spindle speed and access times for hard drives - that is massively irrelevant, because we are not timing that. The OP asked what the most performant way of achieving his task is. As you can see clear as day here, it's to use File.Exists. This is repeatable.:

Actual recorded performance results: try/catch vs File.Exists for non-existent files

Number of files (non-existent): 10,000

Source: http://pastebin.com/6KME40md

Results:

RemoveLocalFiles1 (try/catch) : 119ms

RemoveLocalFiles2 (File.Exists) : 106ms

Community
  • 1
  • 1
Kieren Johnstone
  • 41,277
  • 16
  • 94
  • 144
  • 1
    I don't think the performance matters here, the cost of accessing a file on disk is so much slower that the overhead of exceptions is almost unnoticeable — but it _is_ better to use `File.Exists`, for other reasons. – Abel Aug 12 '11 at 08:16
  • 1
    -1. Good conclusion, but wrong reason. Exception have no overhead at all for almost every conceivable purpose (unless you use them for control flow). Especially when we are moving a hard disk drive head. The reason it is better to check the file before deletion is that the deletion could fail for **other reasons** and you sure want an exception to be thrown if it is the case. Also, the code is clearer: it isn't obvious for a reader that `File.Delete` throws an exception if the file doesn't exist. – Alexandre C. Aug 12 '11 at 08:52
  • Exceptions have plenty of overhead. Create a new object, obtain stack traces, look for appropriate catch block, hand over control. File.Exists? Single Windows API call. I don't mention time and I don't mean time. That is what I'm referring to, and I am not wrong. Please remove your -1 – Kieren Johnstone Aug 12 '11 at 09:07
  • 2
    @Kieren: throwing an exception is thousands (millions ?) of times quicker than accessing the hard disk drive and deleting a file: you have to spin a metal disk and move a head to the right place, something which could take up to *a second*. Throwing an exception is a matter of *microseconds* (even nanoseconds). Efficiency is really not a concern here (and should never been before profiling to get the *actual* bottlenecks). – Alexandre C. Aug 12 '11 at 09:11
  • 1
    @Kieren: Also, testing for `File.Exists` has in fact a problem: the file could be created between the call to `File.Exists` and `File.Delete` by another process. But all of this is moot because [no exception is thrown](http://msdn.microsoft.com/en-us/library/system.io.file.delete(v=VS.90).aspx) when the file doesn't exist. – Alexandre C. Aug 12 '11 at 09:13
  • 2
    @Kieren (and @Alex) you may want to [take a look here](http://stackoverflow.com/questions/52312/what-is-the-real-overhead-of-try-catch-in-c). Conclusion: try and catch add little to no overhead, throw adds a lot, but still negligible compared to accessing a file on disk. – Abel Aug 12 '11 at 09:26
  • @Abel: thanks. I never concern myself with such details without profiling, but good to read. – Alexandre C. Aug 12 '11 at 09:26
  • 1
    @Alex - your comments are quite correct, *always* favor an exception over a nasty threading race. However, the OP mentions having a DirectoryNotFound exception case. – Hans Passant Aug 12 '11 at 09:34
  • All: I just tested try/catch vs File.Exists performance. Please see how you're wrong. @Alex - thousands (millions ?) of times quicker? Try "a little bit slower". Please remove your ill-considered "-1s" – Kieren Johnstone Aug 12 '11 at 11:44
  • @Kieren: 13ms is measurement noise, and this is too much platform dependent. The file allocation table could be in cache, or the garbage collector could pop up at the wrong time, checking for existence when there are only a few non existent files will occur more overhead (after all you don't know OP's use case), etc. Anyways, using `File.Exists` is plain wrong because of the race condition. Accessing the disk **is** thousands of times slower than throwing an exception, even if .NET exceptions really suck performancewise. I would -2 if I could, since you advocate premature optimization. – Alexandre C. Aug 12 '11 at 12:15
  • @Alex - **Try it yourself** on 100, 10000, 100000 files. It's the same every time. That is not measurement noise. Whether or not it's a cache from the file system is irrelevant, the results speak for themselves (we're not writing drivers here). My point was that File.Exists was more efficient, and it is. In the comments, people argued that wasn't the case. It is, I have proven it. My answer is quite correct: *"In terms of efficiency, your definition isn't very clear, but performance and memory-wise, go for File.Exists."*. **Proven. Correct. See the figures.** – Kieren Johnstone Aug 12 '11 at 12:19
  • @Kieren Johnstone: Check it with 5000 existing files (flush the disk after you create them), and 5000 non existing files. You'll see absolutely no difference. You'll see with 10000 files that try/catch is better since it doesn't do 2 round trips to disk. Try also on a networked filesystem, where millions become billions. – Alexandre C. Aug 12 '11 at 12:24
  • @Alex - I've proven my point, put the effort in and come up with an actual test case. If you want to do the same go ahead. – Kieren Johnstone Aug 12 '11 at 12:25
  • @Kieren: http://pastebin.com/6a5jSg5k The results are often different (lots of noise, cache interaction). 10k existing files: try/catch = 1129ms, File.Exists = 1375ms. This doesn't change when reversing the test orders (measurement noise is around 100ms though). When you really access the disk, things change. Anyway, advising premature optimization deserves -1, especially with artificial benchmarks to the rescue. – Alexandre C. Aug 12 '11 at 12:34
  • @Alex and Kieren: aren't you forgetting that everybody said that try/catch added overhead, but only negligibly so, which is what the figures prove? And you aren't measuring the disk cache, which is very fast (esp if you first to a File.Exist and then a Delete on the same file, which both accesses the file table only, not the file itself). To rule that out, try networked files or from a slow USB stick, but even then, it's hard to do a good performance measurement. And what about only two or three files missing, and the rest available? Or from very diverse locations on disk? Not a simple task. – Abel Aug 12 '11 at 16:34
  • @Abel: if you read correctly, this is exactly my point (and the reason I downvoted the answer). – Alexandre C. Aug 15 '11 at 22:14
1

You should use File.Exists, but handle the exception anyway. In general it may be possible for the file to be deleted in between the two calls. Handling the exception still has some overhead, but checking the file for existence first reduces the throw frequency to almost-never. For the one in a million chance that the above scenario occurs, you could still ruin someone's work with an unhandled exception.

Tom W
  • 5,108
  • 4
  • 30
  • 52
1

In the general case, it's indeed better to test for the exceptional case before calling the method, as exceptions should not be used for control flow.

However, we're dealing with the filesystem here, and even if you check that the file exists before deleting it, something else might remove it between your two calls. In that case, Delete() would still throw an exception even if you explicitly made sure it wouldn't.

So, in that particular case, I would be prepared to handle the exception anyway.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • 1
    +1. If `File.Exists` were actually to throw an exception when the file doesn't exist (which [it doesn't](http://msdn.microsoft.com/en-us/library/system.io.file.delete(v=VS.90).aspx)), then the best way is to catch the *specific* exception thrown, since, as you point out, there is a race condition. – Alexandre C. Aug 12 '11 at 09:20
1

I think you should not only worry about efficiency, but also intention. Ask yourself questions like

  • Is it a faulty case that the list of files contains files that don't exist?
  • Do you bother about any of the other errors that can be the cause of failing to delete the file?
  • Should the process continue if errors other than "file not found" occurs?

Obviously, the Delete call can fail even if the file exists, so only adding that check will not protect your code from failure; you still need to catch exception. The question is more about what exceptions to catch and handle, and which ones should bubble upwards to the caller.

Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • A very good point. I'll add some more background to the question. – TeamWild Aug 12 '11 at 08:19
  • "Do you bother about any of the other errors that can be the cause of failing to delete the file?" Of course you do, and you surely don't want to handle them. Most of them warrant program termination. – Alexandre C. Aug 12 '11 at 09:09
  • @Alexandre: exactly my point, even though program termination might not be warranted. Let's say one of the files are in use; depending on context it could be perfectly valid to delete the other files and informing the user that some files could not be deleted. – Fredrik Mörk Aug 12 '11 at 09:21
  • @Fredrik: sonce a program should generally only delete files it created, this would mean there is a logic problem in your application (perhaps you forgot to close the file, or to use `using`). But your point is good. – Alexandre C. Aug 12 '11 at 09:22