C# Story

Chapter #12 – FileSystem Library

System.IO: FileInfo, DirectoryInfo, enumeration, FileSystemWatcher

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