在我写的“推箱子”程序的 DataFile 类中有下面这么一个方法:
该方法主要用在“编辑”关卡完成后保存数据时删除本关的通关步骤(因为关卡地图都被修改了,原来的通关步骤当然不再适用了)。然而,在一次测试中,发现“编辑”关卡完成后保存数据时居然引发一个“DirectoryNotFoundException”异常。经过查找原因,发现通关步骤文件是保存在“steps”目录下,由于从来没有保存过通关步骤,因此也就没有创建“steps”目录,File.Delete 方法在指定的文件不存在时并不引发异常, 但是如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。
后来,将 DataFile.DeleteStepsFile 方法改为下面这个样子就正常了(请参见“使用 C# 开发智手机软件:推箱子(十)”):
我们来看看 MSDN 上对“File.Delete 方法”的描述:
还有“File.Exists 方法”(该方法不会引发异常):
确定指定的文件是否存在。
命名空间: System.IO
程序集: mscorlib(在 mscorlib.dll 中)
public static bool Exists (
string path
)
path 要检查的文件。
如果调用方具有要求的权限并且 path 包含现有文件的名称,则为 true;否则为 false。如果 path 为 空引用(在 Visual Basic 中为 Nothing)、无效路径或零长度字符串,则此方法也将返回 false。如果调用方不具有读取指定文件所需的足够权限,则不引发异常并且该方法返回 false,这与 path 是否存在无关。
不应使用 Exists 方法来验证路径,此方法仅检查 path 中指定的文件是否存在。将无效路径传递到 Exists 将返回 false。
请注意,在您调用 Exists 方法和对文件执行其他操作(如 Delete)之间,其他进程可能会对文件进行一些处理。建议的编程做法是在 try...catch 块中包装 Exists 方法和对文件采取的操作,如示例中所示。这有助于缩小潜在冲突的范围。Exists 方法只能帮助确保文件是可用的,但无法保证。
允许 path 参数指定相对或绝对路径信息。相对路径信息被解释为相对于当前工作目录。若要获取当前工作目录,请参见 GetCurrentDirectory。
如果 path 描述一个目录,则此方法返回 false。在确定文件是否存在之前,从 path 参数中移除尾随空格。
现在我们用下面这段程序来测试一下:
运行结果如下:
可以看出,如果先调用 File.Exists 方法判断一下指定的文件是否存在再决定是否调用 File.Delete 方法,则仅仅在“指定的文件正在使用中”和“指定一个只读文件”这两种情况下会引发异常。而如果直接调用 File.Delete 方法,则在“指定的文件不存在”的情况下不引发异常,但是在“指定的路径无效”的情况下会引发异常。
实际上,我认为,“指定的路径无效”应该也算“指定的文件不存在”的一种情况。所以,FCL 中的 File.Delete 方法如果按以下原则进行设计则对开发人员更为友好:
1. File.Delete 方法在“指定的文件不存在”时引发 FileNotFoundException 异常。
2. File.Delete 方法在“指定的文件不存在”和“指定的路径无效”时不引发异常。
我更倾向于第二种方案。这样,在大多数情况下,就可以直接调用 File.Delete 方法,而不用先调用 File.Exists 方法。
续篇:Linux 操作系统下 File.Delete 的表现
/// <summary>
/// 删除通关步骤文件
/// </summary>
/// <param name="level">关数</param>
private void DeleteStepsFile(int level)
{
File.Delete(GetStepsFileName(fileName, level));
}
/// 删除通关步骤文件
/// </summary>
/// <param name="level">关数</param>
private void DeleteStepsFile(int level)
{
File.Delete(GetStepsFileName(fileName, level));
}
该方法主要用在“编辑”关卡完成后保存数据时删除本关的通关步骤(因为关卡地图都被修改了,原来的通关步骤当然不再适用了)。然而,在一次测试中,发现“编辑”关卡完成后保存数据时居然引发一个“DirectoryNotFoundException”异常。经过查找原因,发现通关步骤文件是保存在“steps”目录下,由于从来没有保存过通关步骤,因此也就没有创建“steps”目录,File.Delete 方法在指定的文件不存在时并不引发异常, 但是如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。
后来,将 DataFile.DeleteStepsFile 方法改为下面这个样子就正常了(请参见“使用 C# 开发智手机软件:推箱子(十)”):
/// <summary>
/// 删除通关步骤文件
/// </summary>
/// <param name="level">关数</param>
private void DeleteStepsFile(int level)
{
// 虽然 File.Delete(): 删除指定的文件。如果指定的文件不存在,则不引发异常。
// 但是: 如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。
// 所以需要先用 File.Exists() 判断一下文件是否存在
string name = GetStepsFileName(fileName, level);
if (File.Exists(name)) File.Delete(name);
}
/// 删除通关步骤文件
/// </summary>
/// <param name="level">关数</param>
private void DeleteStepsFile(int level)
{
// 虽然 File.Delete(): 删除指定的文件。如果指定的文件不存在,则不引发异常。
// 但是: 如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。
// 所以需要先用 File.Exists() 判断一下文件是否存在
string name = GetStepsFileName(fileName, level);
if (File.Exists(name)) File.Delete(name);
}
我们来看看 MSDN 上对“File.Delete 方法”的描述:
还有“File.Exists 方法”(该方法不会引发异常):
确定指定的文件是否存在。
命名空间: System.IO
程序集: mscorlib(在 mscorlib.dll 中)
string path
)
请注意,在您调用 Exists 方法和对文件执行其他操作(如 Delete)之间,其他进程可能会对文件进行一些处理。建议的编程做法是在 try...catch 块中包装 Exists 方法和对文件采取的操作,如示例中所示。这有助于缩小潜在冲突的范围。Exists 方法只能帮助确保文件是可用的,但无法保证。
允许 path 参数指定相对或绝对路径信息。相对路径信息被解释为相对于当前工作目录。若要获取当前工作目录,请参见 GetCurrentDirectory。
如果 path 描述一个目录,则此方法返回 false。在确定文件是否存在之前,从 path 参数中移除尾随空格。
现在我们用下面这段程序来测试一下:
using System;
using System.IO;
sealed class Test
{
static void Main(string[] args)
{
try
{
Console.Write("请输入要删除的文件名: ");
string fileName = Console.ReadLine();
if (fileName == "null") fileName = null;
if (args.Length < 1 || File.Exists(fileName))
{
File.Delete(fileName);
Console.WriteLine("File.Delete 成功");
}
}
catch (Exception ex)
{
Console.WriteLine("错误: " + ex.ToString());
}
}
}
using System.IO;
sealed class Test
{
static void Main(string[] args)
{
try
{
Console.Write("请输入要删除的文件名: ");
string fileName = Console.ReadLine();
if (fileName == "null") fileName = null;
if (args.Length < 1 || File.Exists(fileName))
{
File.Delete(fileName);
Console.WriteLine("File.Delete 成功");
}
}
catch (Exception ex)
{
Console.WriteLine("错误: " + ex.ToString());
}
}
}
运行结果如下:
文件名 | 直接调用 File.Delete 方法 D:\CS\work>test |
先调用 File.Exists 方法 D:\CS\work>test with File.Exists |
零长度字符串 | 请输入要删除的文件名: 错误: System.ArgumentException: 路径的形式不合法。 在 System.IO.Path.NormalizePathFast(String path, Boolean fullCheck) 在 System.IO.Path.GetFullPathInternal(String path) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: |
非法字符 | 请输入要删除的文件名: | 错误: System.ArgumentException: 路径中具有非法字符。 在 System.IO.Path.CheckInvalidPathChars(String path) 在 System.IO.Path.NormalizePathFast(String path, Boolean fullCheck) 在 System.IO.Path.GetFullPathInternal(String path) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: | |
空引用 | 请输入要删除的文件名: null 错误: System.ArgumentNullException: 值不能为空。 参数名: path 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: null |
无效的路径 | 请输入要删除的文件名: none\a.txt 错误: System.IO.DirectoryNotFoundException: 未能找到路径“D:\CS\work\none\a.txt”的一部分。 在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: none\a.txt |
无效的网络路径 | 请输入要删除的文件名: \\z\a.txt 错误: System.IO.IOException: 找不到网络路径。 在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: \\z\a.txt |
格式无效 | 请输入要删除的文件名: ab: 错误: System.NotSupportedException: 不支持给定路径的格式。 在 System.Security.Util.StringExpressionSet.CanonicalizePath(String path, Boolean needFullPath) 在 System.Security.Util.StringExpressionSet.CreateListFromExpressions(String[] str, Boolean needFullPath) 在 System.Security.Permissions.FileIOPermission.AddPathList(FileIOPermissionAccess access, AccessControlActions control, String[] pathListOrig, Boolean checkForDuplicates, Boolean needFullPath, Boolean copyPathList) 在 System.Security.Permissions.FileIOPermission..ctor(FileIOPermissionAccess access, String[] pathList, Boolean checkForDuplicates, Boolean needFullPath) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: ab: |
文件名太长 | 请输入要删除的文件名: -this-string's-length-is-249- 错误: System.IO.PathTooLongException: 指定的路径或文件名太长,或者两者都太长。完全限定文件名必须少于 260 个字符,并且目录名必须少于 248 个字符。 在 System.IO.Path.NormalizePathFast(String path, Boolean fullCheck) 在 System.IO.Path.GetFullPathInternal(String path) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: -this-string's-length-is-249- |
正在使用的文件 | 请输入要删除的文件名: test.exe 错误: System.UnauthorizedAccessException: 对路径“D:\CS\work\test.exe”的访问被拒绝。 在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: test.exe 错误: System.UnauthorizedAccessException: 对路径“D:\CS\work\test.exe”的访问被拒绝。 在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
一个目录 | 请输入要删除的文件名: D:\CS 错误: System.UnauthorizedAccessException: 对路径“D:\CS”的访问被拒绝。 在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: D:\CS |
只读文件 | 请输入要删除的文件名: readonly.file 错误: System.UnauthorizedAccessException: 对路径“D:\CS\work\readonly.file”的访问被拒绝。 在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
请输入要删除的文件名: readonly.file 错误: System.UnauthorizedAccessException: 对路径“D:\CS\work\readonly.file”的访问被拒绝。 在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 在 System.IO.File.Delete(String path) 在 Test.Main(String[] args) |
不存在的文件 | 请输入要删除的文件名: none.file File.Delete 成功 |
请输入要删除的文件名: none.file |
正常的文件 | 请输入要删除的文件名: readwrite1.file File.Delete 成功 |
请输入要删除的文件名: readwrite2.file File.Delete 成功 |
可以看出,如果先调用 File.Exists 方法判断一下指定的文件是否存在再决定是否调用 File.Delete 方法,则仅仅在“指定的文件正在使用中”和“指定一个只读文件”这两种情况下会引发异常。而如果直接调用 File.Delete 方法,则在“指定的文件不存在”的情况下不引发异常,但是在“指定的路径无效”的情况下会引发异常。
实际上,我认为,“指定的路径无效”应该也算“指定的文件不存在”的一种情况。所以,FCL 中的 File.Delete 方法如果按以下原则进行设计则对开发人员更为友好:
1. File.Delete 方法在“指定的文件不存在”时引发 FileNotFoundException 异常。
2. File.Delete 方法在“指定的文件不存在”和“指定的路径无效”时不引发异常。
我更倾向于第二种方案。这样,在大多数情况下,就可以直接调用 File.Delete 方法,而不用先调用 File.Exists 方法。
续篇:Linux 操作系统下 File.Delete 的表现