我在删除文件后刷新文件列表时遇到了问题。当我下达删除文件的命令时,会抛出异常,因为刷新方法试图访问应该已被删除的文件。
经过一番思考和调试,我得出结论,问题在于系统需要一些时间来删除文件。我是这样解决它的:
//Deleting file
System.Threading.Thread.Sleep(2000);
//Refreshing list
它运行得很好。
我的问题是
是否有更优雅的方法来等待系统删除文件,然后继续执行代码...?
我在删除文件后刷新文件列表时遇到了问题。当我下达删除文件的命令时,会抛出异常,因为刷新方法试图访问应该已被删除的文件。
经过一番思考和调试,我得出结论,问题在于系统需要一些时间来删除文件。我是这样解决它的:
//Deleting file
System.Threading.Thread.Sleep(2000);
//Refreshing list
它运行得很好。
我的问题是
是否有更优雅的方法来等待系统删除文件,然后继续执行代码...?
这对我行得通:
public static void DeleteFile(String fileToDelete)
{
var fi = new System.IO.FileInfo(fileToDelete);
if (fi.Exists)
{
fi.Delete();
fi.Refresh();
while (fi.Exists)
{ System.Threading.Thread.Sleep(100);
fi.Refresh();
}
}
}
我发现大多数时候 while 循环将不会被执行。
fi.Delete();
正在使用中,它会抛出一个异常。 - Mark Schultheiss轻量级代码使用FileSystemWatcher,订阅其Deleted
事件并等待。
void DeleteFileAndWait(string filepath, int timeout = 30000)
{
using (var fw = new FileSystemWatcher(Path.GetDirectoryName(filepath), Path.GetFileName(filepath)))
using (var mre = new ManualResetEventSlim())
{
fw.EnableRaisingEvents = true;
fw.Deleted += (object sender, FileSystemEventArgs e) =>
{
mre.Set();
};
File.Delete(filepath);
mre.Wait(timeout);
}
}
Deleted
事件。这里是使用FileWatcher的一些代码。我们想要做的是:
await Utils.DeleteDirectoryAsync("c:\temp\foo", recurse: true);
using System;
using System.IO;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;
namespace Utils
{
internal class FileWatcher : IDisposable
{
readonly FileSystemWatcher _Watcher;
public Subject<FileSystemEventArgs> Changed = new Subject<FileSystemEventArgs>();
public FileWatcher( string file )
{
// Create a new FileSystemWatcher and set its properties.
_Watcher = new FileSystemWatcher
{
Path = Path.GetDirectoryName(file),
NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName,
Filter =Path.GetFileName(file)
};
// Add event handlers.
_Watcher.Changed += OnChanged;
_Watcher.Created += OnChanged;
_Watcher.Deleted += OnChanged;
_Watcher.Renamed += OnChanged;
// Begin watching.
_Watcher.EnableRaisingEvents = true;
}
// Define the event handlers.
private void OnChanged( object source, FileSystemEventArgs e )
{
Changed.OnNext(e);
}
public void Dispose()
{
_Watcher.Dispose();
}
}
}
还有一些工具可以利用上述可观察对象。
public static class FileUtils
{
public static IObservable<FileSystemEventArgs> ChangedObservable(string path)
{
if (path == null)
return Observable.Never<FileSystemEventArgs>();
return Observable.Using(() => new FileWatcher(path), watcher => watcher.Changed);
}
public static Task DeleteDirectoryAsync(string path, bool recurse)
{
var task = new TaskCompletionSource<Unit>();
if (Directory.Exists(path))
{
ChangedObservable(path)
.Where(f => f.ChangeType == WatcherChangeTypes.Deleted)
.Take(1)
.Subscribe(v => task.SetResult(Unit.Default));
Directory.Delete(path, recurse);
}
else
{
task.SetResult(Unit.Default);
}
return task.Task;
}
}
Directory.Delete在遇到第一个错误时会抛出异常。如果你想要继续删除尽可能多的文件和子目录,那么就不应该使用Directory.Delete,而是应该编写自己的递归删除,并在循环内部使用try/catch块。一个需要这样做的例子是,当你试图清理临时文件并且其中一个文件被锁定时。
使用Directory.Delete,特别是使用传入一个布尔值的重载方法,在NTFS上删除目录应该从程序的角度来看是一种原子操作。无需自己手动递归。