如何在不删除 desitnation 或使用 pinvoke 的情况下重命名 .net 中现有文件的文件?

How do I rename a file over an existing file in .net without deleting the desitnation or using pinvoke?

我正在寻找如何在 .net 中执行简单的事务性 POSIX 风格 rename()

If the link named by the new argument exists, it shall be removed and old renamed to new. In this case, a link named new shall remain visible to other processes throughout the renaming operation and refer either to the file referred to by new or old before the operation began.

我不应该在重命名要替换它的新文件之前删除目标文件。删除然后重命名意味着新名称可能 return 在非零时间段内“找不到文件”,如果进程或机器此时崩溃,情况会更糟。

重点是实现简单的事务性文件更新模式,如果我可以先将替换文件成功写入磁盘,我只想替换文件。我真的希望支持这样一个基本的文件系统访问操作不需要 pinvoke 或任何花哨的东西。我应该能够:

  1. 写一个新文件。
  2. 关闭它的 FileStream 以刷新它。
  3. 在旧文件上重命名该文件(假设 FS 将在其日志的帮助下保证此步骤不会持续到文件实际刷新到硬件并且此重命名操作本身是原子的).

.net 的 BCL 中隐藏的重命名功能在哪里?它不在那里吗?如果不存在,是因为 .net 是在 Windows 9x 仍然是受支持的环境时编写的吗?还是现代 Windows 仍然不支持非特权进程的此类操作?

显然这只是一个命名问题。直到我写完这个问题并遇到this answer to another post,我才知道要寻找一个名为File.Replace()的方法。以原子方式将位于 oldPath 的文件移动到位于 newPath:

的另一个现有文件之上
File.Replace(oldPath, newPath, null);

File.Replace() 的文档中有这些有趣的花絮:

PlatformNotSupportedException | The operating system is Windows 98 Second Edition or earlier and the files system is not NTFS.

If the sourceFileName and destinationFileName are on different volumes, this method will raise an exception.

关于不支持非日志文件系统或Windows 98 的说明,它不能跨卷工作意味着该方法不会静默调用File.Copy() 后跟 File.Delete()——它不会默默地将您打算成为原子的操作转换为不安全的操作。

但是,与 rename() 不同的是,如果目标文件不存在,此方法将失败。但是,您可以使用 File.Move() 拒绝覆盖现有文件这一事实以及 File.Replace() 可以事务性地重命名现有文件的某些内容来执行重命名操作,虽然有时可能会失败,但至少不要让文件处于无效状态。如果保持文件处于一致状态比防止程序崩溃更重要,您可以使用:

static void ExceptionalRename(string oldPath, string newPath)
{
    if (File.Exists(newPath))
        File.Replace(oldPath, newPath, null);
    else
        File.Move(oldPath, newPath);
}

不过,我希望我知道 .net 中的某些功能更像开箱即用的 rename()。至少 File.Replace() 提供了对现有文件的事务性重命名,我认为这是 .net 中完全缺失的文件。