1. Introduction
Working directly with streams (StreamReader, StreamWriter, FileStream) is very useful, especially when you need low-level control over reading/writing: for example, writing chunks little by little or processing big files in parts.
But in real life you often just need to know: "does this file exist?", "copy a file from one place to another", "delete a file", "get a list of all .txt files in a folder", and so on. For these tasks Microsoft made two general-purpose tools: the static classes File and Directory. Static means you don't create an instance — you call the needed method directly on the class name.
Life analogy
If streams are you with a pencil carefully writing every letter on a sheet of paper, then File and Directory are your desk with drawers: you can "instantly" open a drawer (Directory), pull out a paper (File.ReadAllText), throw the paper away (File.Delete) or even pull out a whole drawer and move it across the room (Directory.Move). Simple tasks — simple methods.
2. The File class: what it is and what it's for
The File class has a very handy set of static methods for all common file operations.
Most commonly used methods
| Method | Description |
|---|---|
|
Checks if a file exists at the path |
|
Reads the whole file as text |
|
Reads all lines of a file into an array |
|
Overwrites a file by writing text |
|
Appends text to the end of a file |
|
Copies a file |
|
Deletes a file |
|
Moves or renames a file |
|
Opens a stream for advanced operations |
Quick example: check a file and read it
string filePath = "data.txt";
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath);
Console.WriteLine("File content:");
Console.WriteLine(content);
}
else
{
Console.WriteLine("File not found!");
}
See? No manual stream juggling for the simple case!
3. The Directory class: manage folders without pain
If File is about individual files, then Directory is about folders (directories).
Main tasks
| Method | Description |
|---|---|
|
Checks if a folder exists |
|
Creates a folder (or the path with all intermediate folders) |
|
Deletes a folder (optionally with subdirectories) |
|
Gets the list of files in a folder |
|
Gets the list of subdirectories |
|
Moves or renames a folder |
|
Get the application's current working directory |
Example: create a folder and save a file there
string dirPath = "Results";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
Console.WriteLine("Folder 'Results' created.");
}
string filePath = Path.Combine(dirPath, "summary.txt");
File.WriteAllText(filePath, "Final data: ...");
Console.WriteLine($"File {filePath} written.");
4. Typical usage scenarios for File and Directory
Basic methods might seem boring until you see them in action! Let's look at how to use these classes for real tasks and, while we're at it, continue evolving our hypothetical app (imagine we're building a simple todo-list system — we save results to files).
Saving data to a file (replace the old file)
// Save the todo list to a file
string[] todos = { "Kupit hleb", "Pozvonit vrachu", "Sdelat domashku po C#" };
string filePath = "todo.txt";
File.WriteAllLines(filePath, todos);
Console.WriteLine("Todo list saved.");
Loading data from a file
// Load tasks if the file exists
if (File.Exists(filePath))
{
string[] loadedTodos = File.ReadAllLines(filePath);
Console.WriteLine("Your todo list:");
foreach (var task in loadedTodos)
{
Console.WriteLine("- " + task);
}
}
else
{
Console.WriteLine("You don't have a todo list yet.");
}
Adding a new task (without overwriting the file)
string newTask = "Pogulyat s sobakoy";
File.AppendAllText(filePath, newTask + Environment.NewLine);
Console.WriteLine("Task added!");
Copying and backing up a file
string backupPath = "todo_backup.txt";
File.Copy(filePath, backupPath, overwrite: true);
Console.WriteLine("Backup of the todo list created.");
Moving (or renaming) a file
string archivePath = "todo_archive.txt";
File.Move(filePath, archivePath);
Console.WriteLine("Todo list archived (renamed).");
Deleting a file
if (File.Exists(archivePath))
{
File.Delete(archivePath);
Console.WriteLine("Archive deleted — freeing up space!");
}
5. Working with folders: examples
Creating a directory hierarchy
string path = Path.Combine("Reports", "2024", "June");
Directory.CreateDirectory(path);
Console.WriteLine($"Folder {path} created (including intermediate folders).");
Getting the list of files and folders
string dirPath = "Reports";
if (Directory.Exists(dirPath))
{
string[] files = Directory.GetFiles(dirPath);
Console.WriteLine("Files in folder:");
foreach (var file in files)
{
Console.WriteLine(file);
}
string[] subDirs = Directory.GetDirectories(dirPath);
Console.WriteLine("Subdirectories:");
foreach (var dir in subDirs)
{
Console.WriteLine(dir);
}
}
Filtering files by pattern (only txt)
string[] txtFiles = Directory.GetFiles(dirPath, "*.txt");
Console.WriteLine("Only .txt files:");
foreach (var file in txtFiles)
{
Console.WriteLine(file);
}
Recursive traversal (files in all subfolders)
string[] allFiles = Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories);
Console.WriteLine("Files in all nested folders:");
foreach (var file in allFiles)
{
Console.WriteLine(file);
}
Moving and deleting directories
string from = "OldReports";
string to = "Archive/OldReports";
if (Directory.Exists(from))
{
Directory.Move(from, to);
Console.WriteLine($"Folder {from} moved to {to}");
}
if (Directory.Exists(to))
{
Directory.Delete(to, recursive: true); // true: delete everything inside
Console.WriteLine($"Folder {to} deleted (with all contents).");
}
6. Useful nuances
Integration with paths: the Path class
When working with files and folders you often need to build full paths, join directory and file names, parse extensions, or check valid characters. For that you use the helper class Path.
Examples
string folder = "Results";
string filename = "week1.txt";
string fullPath = Path.Combine(folder, filename); // safe joining!
Console.WriteLine(fullPath); // "Results/week1.txt" (or "\" on Windows)
- Get file extension: Path.GetExtension(fullPath)
- Get file name without path: Path.GetFileName(fullPath)
Use Path.Combine instead of string concatenation — it's safer for cross-platform apps.
"All at once" methods vs streaming variants
Many File and Directory methods (like File.ReadAllText, File.WriteAllText, Directory.GetFiles) do the job "all at once": they read/write/get the list in one operation. That's VERY convenient but not suitable for very large files or directories where you might run out of memory. If you're dealing with gigabytes — switch to stream classes (StreamReader, FileStream) or process files in chunks.
When to use File/Directory and when to use streams
| Situation | What to use |
|---|---|
| You need to check if a file/folder exists | |
| Read/write a small whole file | |
| Append text to the end of a file | |
| Get a list of files in a directory | |
| Copy, delete, move a file | |
| Copy/delete a whole folder | Directory.Delete/Move/Copy (Copy — via a third-party library) |
| Process VERY large files in parts | Stream classes (StreamReader, FileStream) |
Practical meaning and real-world uses
- Quick load/save of configurations, settings, JSON/XML files.
- Logging systems (writing logs to disk).
- Simple backup and migration utilities.
- Scanning folders with media, photos, documents.
- User scenarios: loading templates, export/import, report generation, etc.
Interview tasks also often require using these classes: "Implement a function that computes the total size of all .txt files in a directory", "Make a backup of files into a separate folder", etc.
Visual cheat sheet: which method to use when
graph TD
A[What do you want to do?]
A -->|Check existence| B[File.Exists or Directory.Exists]
A -->|Read/write a file entirely| C[File.ReadAllText/WriteAllText]
A -->|Append data to a file| D[File.AppendAllText]
A -->|Work with folders| E[Directory.CreateDirectory, GetFiles, GetDirectories]
A -->|Copy/delete/move a file| F[File.Copy/Delete/Move]
A -->|Copy/delete a folder entirely| G[Directory.Delete/Move]
7. Peculiarities and common errors
Existence checks and "race conditions". Even if you check that a file/folder exists, another process can change it between that check and the actual operation. So always use try-catch even if you already checked!
Permissions. If the app lacks rights to read/write/delete, methods will throw UnauthorizedAccessException.
Path too long. Maximum path length (usually 260 characters on old Windows; in .NET 9 the limits are softer, but still be careful).
Error handling. Methods like File.ReadAllText or Directory.GetFiles don't forgive errors: if the file/folder is missing — they throw immediately. Wrap calls in try-catch or pre-check existence.
GO TO FULL VERSION