当前位置: 首页 > news >正文

谨慎使用 FileInfo.Exists 实例方法,而是使用 File.Exists 静态方法替代

如果你在代码中使用了 FileInfo.Exists 实例方法来判断一个文件是否存在,也许会发现此方法可能错误地判断来一个文件是否真的存在。这是一个坑。

本文将介绍坑的原因,并提供填坑的办法。


本文内容

      • 问题代码
      • 原因分析
      • 解决办法

问题代码

我们使用两种不同的方式判断文件是否存在:

  • FileInfo.Exists 实例方法
  • File.Exists 静态方法
static async Task Main(string[] args)
{
    var filePath = @"C:\Users\lvyi\Desktop\walterlv.log";
    var fileInfo = new FileInfo(filePath);
    while (true)
    {
        Console.WriteLine($"FileInfo.Exists = {fileInfo.Exists}");
        Console.WriteLine($"    File.Exists = {File.Exists(filePath)}");
        Console.WriteLine("----");
        await Task.Delay(1000);
    }
}

现在运行这个程序,我们会发现,中途删除了 walterlv.log 文件之后,FileInfo.Exists 依然返回了 true,而 File.Exists 已经开始返回 false 了。

以上代码的运行结果

原因分析

实际翻阅代码可以发现,FileInfo.ExistsFile.Exists 方法最终都是使用相同的方法来完成文件存在与否的判断。

这是 FileInfo.Exists 的判断:

public override bool Exists
{
    [SecuritySafeCritical] get
    {
        try
        {
            if (this._dataInitialised == -1)
                this.Refresh();
            if (this._dataInitialised != 0)
                return false;
            return (this._data.fileAttributes & 16) == 0;
        }
        catch
        {
            return false;
        }
    }
}

这是 File.Exists 的最终判断:

public static bool FileExists(string fullPath)
{
    Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA();
    int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true);

    return (errorCode == 0) && (data.dwFileAttributes != -1)
            && ((data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0);
}

只不过,FileInfo.Exists 只会在没有初始化的时候初始化一次,而 File.Exists 是没有缓存的,每次都是直接去获取文件的属性(这就涉及到 IO)。

解决办法

所以,如果你正在处理的文件在不同的时间可能存在也可能不存在,那么最好使用 File.Exists 来判断文件存在与否,而不是使用 FileInfo.Exists 来判断。

不过,如果你需要一次性判断文件的非常多的信息(而不只是文件存在与否),那么依然建议使用 FileInfo,只不过在使用之前需要调用 Refresh 进行一次刷新。


我的博客会首发于 https://walterlv.com/,而 CSDN 和博客园仅从其中摘选发布,而且一旦发布了就不再更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://blog.csdn.net/wpwalter),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • UWP 在 WebView 中执行 JavaScript 代码(用于模拟用户输入等)
  • .NET 中使用 Mutex 进行跨越进程边界的同步
  • int? 竟然真的可以是 null!.NET/C# 确定可空值类型 NullableT 实例的真实类型
  • Slack 开发入门之 Incoming Webhooks:往 Slack 的 Channel 中发消息
  • 三值 bool? 进行与或运算后的结果
  • 为什么我们不应该使用微信或者 QQ 作为团队协作的 IM 工具?
  • 通过重写预定义的 Target 来扩展 MSBuild / Visual Studio 的编译过程
  • .NET 中 GetHashCode 的哈希值有多大概率会相同(哈希碰撞)
  • C# 中委托实例的命名规则
  • 在 Target 中获取项目引用的所有依赖(dll/NuGet/Project)的路径
  • 让 MSBuild Target 支持 Clean
  • C#/.NET 如何确认一个路径是否是合法的文件路径
  • 不使用 U 盘等任何工具全新安装 Windows 操作系统
  • C# 永远不会返回的方法真的不会返回
  • CentOS 的终端中如何搜索文件
  • __proto__ 和 prototype的关系
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • magento2项目上线注意事项
  • PHP 的 SAPI 是个什么东西
  • React-Native - 收藏集 - 掘金
  • Redis的resp协议
  • 代理模式
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 二维平面内的碰撞检测【一】
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 那些被忽略的 JavaScript 数组方法细节
  • 驱动程序原理
  • 在weex里面使用chart图表
  • 栈实现走出迷宫(C++)
  • Prometheus VS InfluxDB
  • puppet连载22:define用法
  • raise 与 raise ... from 的区别
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • 如何正确理解,内页权重高于首页?
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • #android不同版本废弃api,新api。
  • #微信小程序:微信小程序常见的配置传值
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (2)STL算法之元素计数
  • (5)STL算法之复制
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (八)Flask之app.route装饰器函数的参数
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (算法二)滑动窗口
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (转)可以带来幸福的一本书
  • **PHP二维数组遍历时同时赋值
  • .Net CF下精确的计时器
  • .NET Framework 4.6.2改进了WPF和安全性
  • .net(C#)中String.Format如何使用
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)