CSharpStory_LibraryFileSystem.html
copyright © James Fawcett
Revised: 05/15/2026
12.0 Prologue
The previous chapter covered stream-oriented I/O. This chapter focuses on the
file-system side of System.IO: the object-oriented FileInfo and
DirectoryInfo wrappers that expose rich metadata, flexible directory
enumeration with EnumerationOptions, reactive monitoring with
FileSystemWatcher, and drive-level information via DriveInfo.
12.1 FileInfo
FileInfo is an object-oriented wrapper around a single file path. Unlike the
static File class, it caches metadata after the first access and exposes
file operations as instance methods:
Key properties:
- Name, FullName, Extension, DirectoryName
- Length — file size in bytes
- CreationTime, LastWriteTime, LastAccessTime
- Exists, IsReadOnly, Attributes
Key methods: CopyTo, MoveTo, Delete,
OpenRead, OpenWrite, OpenText, Refresh.
var fi = new FileInfo("report.txt");
if (fi.Exists) {
Console.WriteLine($"{fi.Name} {fi.Length} bytes {fi.LastWriteTime:g}");
fi.CopyTo("report.bak", overwrite: true);
}
// rename in place
fi.MoveTo(Path.Combine(fi.DirectoryName!, "report_2026.txt"));
Call Refresh() to re-read cached metadata after external changes.
12.2 DirectoryInfo
DirectoryInfo mirrors FileInfo but for directories. It provides
typed enumeration methods that return FileInfo and
DirectoryInfo objects rather than raw strings:
var dir = new DirectoryInfo("./src");
// all .cs files in this directory only
foreach (FileInfo f in dir.GetFiles("*.cs"))
Console.WriteLine($" {f.Name} ({f.Length} bytes)");
// all subdirectories
foreach (DirectoryInfo sub in dir.GetDirectories())
Console.WriteLine($" {sub.Name}/ created {sub.CreationTime:d}");
Create(), Delete(recursive), and MoveTo(dest) are
the primary mutation methods. Parent and Root navigate the tree.
12.3 Enumerating the File System
The static Directory class and the instance DirectoryInfo both
offer Get* (eager array) and Enumerate* (lazy) variants.
Prefer Enumerate* for large trees because it yields results without
loading the entire list into memory:
// recursive enumeration - lazy, memory efficient
foreach (string path in Directory.EnumerateFiles(".", "*.cs",
SearchOption.AllDirectories))
Console.WriteLine(path);
EnumerationOptions (introduced in .NET Core 2.1) gives fine control:
var opts = new EnumerationOptions {
RecurseSubdirectories = true,
IgnoreInaccessible = true, // skip protected dirs silently
MatchCasing = MatchCasing.CaseInsensitive,
AttributesToSkip = FileAttributes.Hidden | FileAttributes.System,
};
foreach (string f in Directory.EnumerateFiles("C:/projects", "*.sln", opts))
Console.WriteLine(f);
DirectoryInfo.EnumerateFileSystemInfos() returns a mixed sequence of
FileInfo and DirectoryInfo objects, useful when you need to
process files and directories in the same pass.
12.4 FileSystemWatcher
FileSystemWatcher raises events when files or directories change. It uses
OS-level change notifications rather than polling:
using var watcher = new FileSystemWatcher("./logs") {
Filter = "*.log",
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName,
IncludeSubdirectories = false,
EnableRaisingEvents = true,
};
watcher.Changed += (_, e) => Console.WriteLine($"changed: {e.FullPath}");
watcher.Created += (_, e) => Console.WriteLine($"created: {e.FullPath}");
watcher.Deleted += (_, e) => Console.WriteLine($"deleted: {e.FullPath}");
watcher.Renamed += (_, e) => Console.WriteLine($"renamed: {e.OldFullPath} -> {e.FullPath}");
watcher.Error += (_, e) => Console.WriteLine($"error: {e.GetException().Message}");
Console.ReadLine(); // keep alive
NotifyFilter flags control which OS-level attributes trigger events.
The internal buffer defaults to 8 KB; increase InternalBufferSize in
high-volume scenarios to avoid lost events.
12.5 DriveInfo
DriveInfo reports the mounted volumes visible to the process:
foreach (DriveInfo drive in DriveInfo.GetDrives()) {
Console.Write($"{drive.Name} {drive.DriveType,-12}");
if (drive.IsReady) {
long gb = drive.TotalSize / (1024L * 1024 * 1024);
long free = drive.AvailableFreeSpace / (1024L * 1024 * 1024);
Console.Write($" {gb} GB total {free} GB free [{drive.VolumeLabel}]");
}
Console.WriteLine();
}
Check IsReady before reading size or label properties; removable drives
may be absent.
12.6 File Attributes and Timestamps
FileAttributes is a flags enum covering ReadOnly,
Hidden, System, Directory, Archive,
Compressed, Encrypted, and others:
// read
FileAttributes attrs = File.GetAttributes("config.json");
bool isHidden = attrs.HasFlag(FileAttributes.Hidden);
// set / clear
File.SetAttributes("config.json", attrs | FileAttributes.ReadOnly);
File.SetAttributes("config.json", attrs && ~FileAttributes.ReadOnly);
// timestamps
File.SetLastWriteTime("config.json", DateTime.UtcNow);
DateTime created = File.GetCreationTimeUtc("config.json");
12.7 Epilogue
This chapter covered the object-oriented file-system API: FileInfo and
DirectoryInfo for metadata-rich access, lazy enumeration with
EnumerationOptions, reactive monitoring via FileSystemWatcher,
drive inventory with DriveInfo, and file attributes. The next chapter
covers System.Threading.
12.8 References
FileInfo — Microsoft docs
DirectoryInfo — Microsoft docs
FileSystemWatcher — Microsoft docs
EnumerationOptions — Microsoft docs
File system overview — Microsoft docs