1. 介绍
如果把静态类,比如 File 和 Directory 当作一把通用的瑞士军刀:随手可用、能快速解决问题,但不绑定到具体对象上;那么 FileInfo 和 DirectoryInfo 就像专用工具:为某个文件或文件夹创建,"知道"很多关于它们的信息,并且在对同一个对象做多次操作时用起来更方便、更灵活。
实例类 保存关于某个具体文件系统对象的细节,而且在首次加载属性后不必每次都去文件系统读取信息(除非该对象已经过期)。
- FileInfo — 用于操作具体的文件。
- DirectoryInfo — 用于操作具体的目录(文件夹)。
它们包含属性和方法,可以获取详细信息(大小、创建时间、扩展名、可用属性)并对文件/文件夹执行各种操作:复制、删除、移动等。
FileInfo 和 DirectoryInfo 的结构
两个类都继承自一个共同的抽象类 FileSystemInfo,这样可以写出同时处理文件和文件夹的通用代码。
System.Object
|
System.MarshalByRefObject
|
System.IO.FileSystemInfo (抽象类)
| |
FileInfo DirectoryInfo
什么时候使用实例类?
- 当你需要关于某个文件/文件夹的大量信息(大小、修改日期、扩展名、属性等),并且这些数据会被多次使用时。
- 当你想枚举目录中的所有文件/文件夹,并把每个对象当作“活的”实例来处理时。
- 为了优化,避免不必要地访问文件系统(信息缓存很有用)。
- 为了写更面向对象的代码(备份、媒体库、文件管理器等)。
如何创建 FileInfo 或 DirectoryInfo 实例
using System.IO;
// 为一个存在的(或不存在的!)文件创建 FileInfo 对象:
var fileInfo = new FileInfo("notes.txt");
// 为一个文件夹创建 DirectoryInfo 对象:
var dirInfo = new DirectoryInfo(@"C:\Projects");
// 可以使用相对路径或绝对路径!
重要提示:创建 FileInfo 或 DirectoryInfo 对象并不会在文件系统中创建文件/文件夹!它只是一个描述(引用关系)。
2. 使用 FileInfo
我们来看看这个类的主要属性和方法,下面是备忘:
| 属性/方法 | 返回什么? | 使用示例 |
|---|---|---|
|
文件名 | |
|
完整路径 | |
|
文件大小(字节) | |
|
父目录(DirectoryInfo) | |
|
文件是否真实存在? | |
|
文件扩展名(.txt) | |
|
创建日期 | |
|
最后修改日期 | |
|
是否只读? | |
| 方法 | ||
|
复制文件 | |
|
删除文件 | |
|
移动文件 | |
|
打开读取流 | |
|
打开写入流 | |
示例:文件信息
加一个小模块,用 FileInfo 获取文件的详细信息。
Console.WriteLine("请输入文件名:");
string fileName = Console.ReadLine();
var file = new FileInfo(fileName);
if (file.Exists)
{
Console.WriteLine($"文件名: {file.Name}");
Console.WriteLine($"路径: {file.FullName}");
Console.WriteLine($"大小: {file.Length} 字节");
Console.WriteLine($"扩展名: {file.Extension}");
Console.WriteLine($"创建时间: {file.CreationTime}");
Console.WriteLine($"最后修改: {file.LastWriteTime}");
// 另外:显示父目录
Console.WriteLine($"父文件夹: {file.DirectoryName}");
}
else
{
Console.WriteLine("未找到文件。");
}
注意:我们并没有读取文件内容,只是获取元信息。
练习:复制和删除文件
// ... 在主方法中,在成功输出文件信息之后:
Console.WriteLine("输入复制文件的路径:");
string copyPath = Console.ReadLine();
try
{
file.CopyTo(copyPath);
Console.WriteLine($"文件已成功复制到 {copyPath}");
}
catch (IOException ex)
{
Console.WriteLine($"无法复制文件: {ex.Message}");
}
// 现在删除副本
Console.WriteLine("删除副本吗? (y/n):");
if (Console.ReadLine().Trim().ToLower() == "y")
{
var copyFile = new FileInfo(copyPath);
if (copyFile.Exists)
{
copyFile.Delete();
Console.WriteLine("副本已删除。");
}
}
3. 使用 DirectoryInfo
看看 DirectoryInfo 能带来什么便利:
| 属性/方法 | 返回什么? | 使用示例 |
|---|---|---|
|
文件夹名 | |
|
完整路径 | |
|
父文件夹(DirectoryInfo) | |
|
文件夹是否存在? | |
|
创建日期 | |
|
修改日期 | |
| 方法 | ||
|
创建文件夹 | |
|
删除文件夹 | |
|
文件数组(FileInfo 数组) | |
|
子文件夹数组(DirectoryInfo 数组) | |
|
枚举文件(返回 IEnumerable<FileInfo>,延迟模式) | |
|
移动文件夹 | |
示例:遍历文件和文件夹
Console.WriteLine("请输入文件夹路径:");
string path = Console.ReadLine();
var dir = new DirectoryInfo(path);
if (!dir.Exists)
{
Console.WriteLine("未找到文件夹!");
return;
}
Console.WriteLine("\n--- 文件 ---");
foreach (var file in dir.GetFiles())
{
Console.WriteLine($"{file.Name} ({file.Length} 字节)");
}
Console.WriteLine("\n--- 子文件夹 ---");
foreach (var subDir in dir.GetDirectories())
{
Console.WriteLine(subDir.Name);
}
4. 有用的细节
静态方法 vs 实例类
什么时候用哪个?如果你只是做一次性的快速操作——例如 File.Exists(path) 或 File.ReadAllText(path),静态方法更合适。它们更简单,开销更小,速度略快。
但如果你想获取大量关于文件或文件夹的信息、对同一对象做重复操作,或者想用面向对象的风格写代码,最好用实例 FileInfo 和 DirectoryInfo。
有趣的是,两种方式在底层都调用了相似的系统调用。但实例类会缓存某些属性(大小、日期、属性),从而减少对文件系统的访问——在遍历大量文件时特别有用。
关于日期和时间的小提示
属性 CreationTime、LastWriteTime、LastAccessTime 返回的是 DateTime 类型。
Console.WriteLine($"文件创建: {file.CreationTime:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"最后修改: {file.LastWriteTime:yyyy-MM-dd HH:mm:ss}");
如果在你创建 FileInfo 或 DirectoryInfo 实例之后,文件或文件夹在磁盘上被修改了,缓存的信息就可能过时。要刷新它,调用 file.Refresh()。
建议和实战注意事项
- 实例类适合用来构造文件系统的包装:备份、媒体库、索引器等场景。
- 与 LINQ 配合得很好:可以按大小、日期或扩展名过滤,然后对选中的对象执行操作。
- 在批量操作时优先考虑 EnumerateFiles() 和 EnumerateDirectories(),而不是 GetFiles()/GetDirectories()——延迟枚举节省内存并能更快开始处理。
静态方法和实例方法的比较
| 静态方法 | 实例类 | |
|---|---|---|
| 语法 | |
|
| 缓存 | 没有 | 有 |
| 大量信息 | 需要调用很多方法 | 对象里就有 |
| OO 风格 | 没有 | 有 |
| 批量操作 | 不方便 | 方便 |
| 流的操作 | 有 | 有 |
刷新信息和对象
对象 FileInfo/DirectoryInfo 在第一次访问属性时会缓存值。如果在实例创建后文件(或文件夹)在磁盘上被修改,想要更新信息,就调用 .Refresh() 方法。
var file = new FileInfo("notes.txt");
long oldSize = file.Length;
// 这期间别人(或你自己)在程序外修改了文件
file.Refresh();
long newSize = file.Length;
这种情况不常见,但如果你的程序在监视文件系统,刷新机制很重要。
5. 使用实例类时常见的错误
误解:创建对象 new FileInfo("file.txt") 就意味着文件已经存在。实际上不是——文件只有在显式写入或调用 Create() 后才会出现在磁盘上。
尝试访问不存在文件的属性:有些属性会返回默认值(例如大小为 0),但尝试打开读取流会抛出异常。
文件夹也一样:创建 DirectoryInfo 并不保证磁盘上存在该文件夹,除非调用 Create() 或它实际被创建。
GO TO FULL VERSION