我使用。net 3.5,试图递归删除目录使用:
Directory.Delete(myPath, true);
我的理解是,如果文件正在使用或存在权限问题,这应该抛出,但否则它应该删除目录及其所有内容。
然而,我偶尔会遇到这样的情况:
System.IO.IOException: The directory is not empty.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
...
我并不惊讶于这个方法有时会抛出错误,但我惊讶于当递归为真时得到这个特定的消息。(我知道目录不是空的。)
是否有一个原因,我将看到这个而不是AccessViolationException?
上面的答案对我都没用。似乎我自己的应用程序在目标目录上使用DirectoryInfo导致它保持锁定。
强制垃圾收集似乎解决了这个问题,但不是马上就能解决的。在需要的地方尝试删除几次。
注意目录。存在,因为它可以在异常后消失。我不知道为什么我的删除被延迟了(Windows 7 SP1)
for (int attempts = 0; attempts < 10; attempts++)
{
try
{
if (Directory.Exists(folder))
{
Directory.Delete(folder, true);
}
return;
}
catch (IOException e)
{
GC.Collect();
Thread.Sleep(1000);
}
}
throw new Exception("Failed to remove folder.");
现代异步回答
公认的答案是完全错误的,它可能适用于某些人,因为从磁盘获取文件所花费的时间释放了锁定文件的任何东西。事实上,这是因为文件被其他进程/流/操作锁定了。其他答案使用Thread。Sleep (Yuck)在一段时间后重试删除目录。这个问题需要一个更现代的答案来重新审视。
public static async Task<bool> TryDeleteDirectory(
string directoryPath,
int maxRetries = 10,
int millisecondsDelay = 30)
{
if (directoryPath == null)
throw new ArgumentNullException(directoryPath);
if (maxRetries < 1)
throw new ArgumentOutOfRangeException(nameof(maxRetries));
if (millisecondsDelay < 1)
throw new ArgumentOutOfRangeException(nameof(millisecondsDelay));
for (int i = 0; i < maxRetries; ++i)
{
try
{
if (Directory.Exists(directoryPath))
{
Directory.Delete(directoryPath, true);
}
return true;
}
catch (IOException)
{
await Task.Delay(millisecondsDelay);
}
catch (UnauthorizedAccessException)
{
await Task.Delay(millisecondsDelay);
}
}
return false;
}
单元测试
这些测试展示了一个被锁定的文件如何导致目录的示例。删除失败以及上面的TryDeleteDirectory方法如何修复这个问题。
[Fact]
public async Task TryDeleteDirectory_FileLocked_DirectoryNotDeletedReturnsFalse()
{
var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
var filePath = Path.Combine(directoryPath, "File.txt");
try
{
Directory.CreateDirectory(directoryPath);
Directory.CreateDirectory(subDirectoryPath);
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
var result = await TryDeleteDirectory(directoryPath, 3, 30);
Assert.False(result);
Assert.True(Directory.Exists(directoryPath));
}
}
finally
{
if (Directory.Exists(directoryPath))
{
Directory.Delete(directoryPath, true);
}
}
}
[Fact]
public async Task TryDeleteDirectory_FileLockedThenReleased_DirectoryDeletedReturnsTrue()
{
var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
var filePath = Path.Combine(directoryPath, "File.txt");
try
{
Directory.CreateDirectory(directoryPath);
Directory.CreateDirectory(subDirectoryPath);
Task<bool> task;
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
task = TryDeleteDirectory(directoryPath, 3, 30);
await Task.Delay(30);
Assert.True(Directory.Exists(directoryPath));
}
var result = await task;
Assert.True(result);
Assert.False(Directory.Exists(directoryPath));
}
finally
{
if (Directory.Exists(directoryPath))
{
Directory.Delete(directoryPath, true);
}
}
}
编者注:尽管这个答案包含了一些有用的信息,但关于Directory.Delete的工作原理实际上是不正确的。请阅读这个答案的评论,以及这个问题的其他答案。
我以前遇到过这个问题。
The root of the problem is that this function does not delete files that are within the directory structure. So what you'll need to do is create a function that deletes all the files within the directory structure then all the directories before removing the directory itself. I know this goes against the second parameter but it's a much safer approach. In addition, you will probably want to remove READ-ONLY access attributes from the files right before you delete them. Otherwise that will raise an exception.
只需将这些代码放入您的项目中。
public static void DeleteDirectory(string target_dir)
{
string[] files = Directory.GetFiles(target_dir);
string[] dirs = Directory.GetDirectories(target_dir);
foreach (string file in files)
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
foreach (string dir in dirs)
{
DeleteDirectory(dir);
}
Directory.Delete(target_dir, false);
}
另外,对我个人来说,我对允许删除的机器区域添加了一个限制,因为你希望有人在C:\ windows (%WinDir%)或C:\。