在继续之前,检查一下你能控制的以下原因:
该文件夹是否设置为您的进程的当前目录?如果是,请先将其更改为其他内容。
您是否从该文件夹中打开了文件(或加载了DLL) ?(并且忘记关闭/卸载它)
否则,请检查以下不在你控制范围内的合理原因:
该文件夹中有标记为只读的文件。
您没有删除其中一些文件的权限。
文件或子文件夹在资源管理器或其他应用程序中打开。
如果是上述任何一个问题,在尝试改进删除代码之前,您应该了解为什么会发生这种情况。你的应用程序是否应该删除只读或不可访问的文件?是谁给它们做的标记,为什么?
一旦排除了上述原因,仍然存在虚假失败的可能性。如果任何人持有任何被删除的文件或文件夹的句柄,删除将失败,并且有人可能会枚举文件夹或读取其文件的原因有很多:
搜索索引器
要
备份软件
处理虚假失败的一般方法是尝试多次,在尝试之间暂停。显然,您不希望一直尝试下去,因此应该在一定次数的尝试后放弃,并抛出异常或忽略错误。是这样的:
private static void DeleteRecursivelyWithMagicDust(string destinationDir) {
const int magicDust = 10;
for (var gnomes = 1; gnomes <= magicDust; gnomes++) {
try {
Directory.Delete(destinationDir, true);
} catch (DirectoryNotFoundException) {
return; // good!
} catch (IOException) { // System.IO.IOException: The directory is not empty
System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes);
// see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic
Thread.Sleep(50);
continue;
}
return;
}
// depending on your use case, consider throwing an exception here
}
在我看来,像这样的帮助器应该用于所有的删除,因为虚假的失败总是可能的。然而,你应该根据你的用例调整这些代码,而不是盲目地复制它。
我的应用程序生成的内部数据文件夹,位于%LocalAppData%下,所以我的分析如下:
The folder is controlled solely by my application, and the user has no valid reason to go and mark things as read-only or inaccessible inside that folder, so I don't try to handle that case.
There's no valuable user-created stuff in there, so there's no risk of forcefully deleting something by mistake.
Being an internal data folder, I don't expect it to be open in explorer, at least I don't feel the need to specifically handle the case (i.e. I'm fine handling that case via support).
If all attempts fail, I choose to ignore the error. Worst case, the app fails to unpack some newer resources, crashes and prompts the user to contact support, which is acceptable to me as long as it does not happen often. Or, if the app does not crash, it will leave some old data behind, which again is acceptable to me.
I choose to limit retries to 500ms (50 * 10). This is an arbitrary threshold which works in practice; I wanted the threshold to be short enough so that users wouldn't kill the app, thinking that it has stopped responding. On the other hand, half a second is plenty of time for the offender to finish processing my folder. Judging from other SO answers which sometimes find even Sleep(0) to be acceptable, very few users will ever experience more than a single retry.
I retry every 50ms, which is another arbitrary number. I feel that if a file is being processed (indexed, checked) when I try to delete it, 50ms is about the right time to expect the processing to be completed in my case. Also, 50ms is small enough to not result in a noticeable slowdown; again, Sleep(0) seems to be enough in many cases, so we don't want to delay too much.
The code retries on any IO exceptions. I don't normally expect any exceptions accessing %LocalAppData%, so I chose simplicity and accepted the risk of a 500ms delay in case a legitimate exception happens. I also didn't want to figure out a way to detect the exact exception that I want to retry on.