10 questions found
How do you read and write an entire text file's contents using the simplest File class methods?
Beginner
File.ReadAllText() reads an entire file's contents into a single string; File.WriteAllText() writes a string to a file, creating it if it doesn't exist or overwriting it if it does — both are synchronous and load the ENTIRE file into memory at once, fine for small files.
string content = File.ReadAllText("data.txt");
File.WriteAllText("output.txt", "Hello, World!");
// Async equivalents
string content2 = await File.ReadAllTextAsync("data.txt");
await File.WriteAllTextAsync("output.txt", "Hello, World!");
Real-world example
Reading a small configuration file's entire content in one call, without manually managing a stream.
Common follow-ups: Why should you prefer the Async versions of these methods in a server application?
Asynchronous Programming
What is the difference between File.ReadAllLines() and File.ReadLines() in terms of memory usage?
Beginner
File.ReadAllLines() reads the ENTIRE file and returns a fully-materialized string[] array all at once; File.ReadLines() returns a LAZY IEnumerable<string> that reads and yields one line at a time as you iterate, using far less memory for very large files since it never holds the whole file in memory simultaneously.
// Loads the entire file into memory at once
string[] allLines = File.ReadAllLines("huge.txt");
// Reads and processes one line at a time, low memory footprint
foreach (string line in File.ReadLines("huge.txt")) {
ProcessLine(line);
}
Real-world example
Processing a multi-gigabyte log file line by line without loading the entire file into memory.
Common follow-ups: Which method would you choose if you needed to know the total line count before processing?
Iterators & yield return
How does the 'using' statement (or 'using' declaration) ensure a FileStream is properly closed, even if an exception occurs?
Intermediate
'using' automatically calls Dispose() on the resource when execution leaves its scope — whether normally or via an exception — guaranteeing the underlying file handle is released; this is essential for FileStream and other IDisposable resources like database connections, since forgetting to dispose them can leave files locked or exhaust OS handles.
using (FileStream stream = File.OpenRead("data.txt")) {
// stream is guaranteed to be disposed when this block exits, even on exception
}
// Modern C# 8+ 'using declaration' (disposed at end of enclosing scope)
using FileStream stream2 = File.OpenRead("data.txt");
Real-world example
Guaranteeing a file lock is released immediately after processing, even if the processing code throws an exception midway.
Common follow-ups: What interface must a type implement to be usable with a 'using' statement?
Exception Handling
How do StreamReader and StreamWriter differ from directly using a FileStream, and when would you choose them?
Intermediate
FileStream operates on raw BYTES; StreamReader/StreamWriter wrap a Stream (like a FileStream) and add TEXT ENCODING handling (converting between bytes and characters using a specified or detected encoding, like UTF-8), providing convenient line-based text reading/writing methods like ReadLine() and WriteLine().
using StreamReader reader = new StreamReader("data.txt");
string? line;
while ((line = reader.ReadLine()) != null) {
Console.WriteLine(line);
}
using StreamWriter writer = new StreamWriter("output.txt");
writer.WriteLine("Hello, World!");
Real-world example
Reading a text-based configuration or CSV file line by line with automatic character encoding handling.
Common follow-ups: How would you specify a specific text encoding (like UTF-16) when creating a StreamReader?
String Handling & StringBuilder
How do you efficiently copy data between two streams using Stream.CopyToAsync(), and why is this preferred over a manual byte-by-byte loop?
Intermediate
CopyToAsync() efficiently transfers data from a source stream to a destination stream using an internally-managed BUFFER (reading a chunk, writing it, repeating), fully asynchronously — far more efficient and less error-prone than manually implementing your own read/write loop with a byte array.
using FileStream source = File.OpenRead("large-file.dat");
using FileStream destination = File.Create("copy.dat");
await source.CopyToAsync(destination); // efficient, buffered, fully async
Real-world example
Copying a large uploaded file from a temporary location to permanent storage without loading the entire file into memory.
Common follow-ups: Can CopyToAsync() also be used between fundamentally different stream types, like a network stream and a file stream?
Asynchronous Programming
How would you implement a custom Stream subclass to wrap another stream with additional behavior, like counting bytes read?
Advanced
Inherit from Stream, delegate the core required members (Read, Write, Seek, Length, etc.) to an inner wrapped stream, and inject your custom logic (like incrementing a counter) at the specific override points where you need it — this lets your custom stream be used ANYWHERE a Stream is expected, fully transparently.
public class CountingStream : Stream {
private readonly Stream _inner;
public long BytesRead { get; private set; }
public CountingStream(Stream inner) { _inner = inner; }
public override int Read(byte[] buffer, int offset, int count) {
int bytesRead = _inner.Read(buffer, offset, count);
BytesRead += bytesRead;
return bytesRead;
}
// ... delegate remaining required members (CanRead, Length, Seek, etc.) to _inner
}
Real-world example
Tracking upload/download progress transparently by wrapping a network or file stream without modifying the code that consumes it.
Common follow-ups: Which Stream members are ABSOLUTELY required to override versus which have reasonable default implementations already?
Interfaces & Abstract Classes
How do you use FileSystemWatcher to monitor a directory for file changes, and what are its common reliability pitfalls?
Advanced
FileSystemWatcher raises events (Created, Changed, Deleted, Renamed) when files change within a monitored directory — a common pitfall is that a single logical file save can trigger MULTIPLE rapid Changed events (due to how some applications write files), so production code typically needs debouncing logic to avoid processing the same change multiple times.
var watcher = new FileSystemWatcher(@"C:\Data") { EnableRaisingEvents = true };
watcher.Changed += (sender, e) => {
Console.WriteLine($"File changed: {e.FullPath}");
// In production: debounce here, since this can fire multiple times per actual save
};
Real-world example
Building a 'hot reload' feature that automatically reprocesses a configuration file whenever it's edited on disk.
Common follow-ups: What's the recommended approach for debouncing FileSystemWatcher's often-duplicate Changed events?
Multithreading & Task Parallel Library
How would you implement memory-mapped file access using MemoryMappedFile for efficient random access to a very large file?
Advanced
MemoryMappedFile maps a file's contents directly into the process's virtual address space, letting you access arbitrary portions of even a very large file (larger than available RAM) as if it were an in-memory array, with the OS handling paging data in/out transparently — far more efficient than repeated Seek/Read calls for random-access patterns.
using var mmf = MemoryMappedFile.CreateFromFile("huge-file.dat", FileMode.Open);
using var accessor = mmf.CreateViewAccessor(0, 1000);
byte firstByte = accessor.ReadByte(0);
accessor.Write(0, (byte)42); // efficient random access, OS-managed paging
Real-world example
Efficiently performing random-access reads/writes on a multi-gigabyte database or index file without loading it entirely into memory.
Common follow-ups: Why is memory-mapped access particularly well-suited for scenarios needing frequent RANDOM (not sequential) access to a huge file?
Memory & Garbage Collection
How do you correctly implement async, cancellable file I/O using CancellationToken with FileStream operations?
Advanced
Pass a CancellationToken through to the async overloads of ReadAsync/WriteAsync/CopyToAsync — these methods periodically check for cancellation and throw an OperationCanceledException promptly if the token is signaled mid-operation, letting long-running file I/O be cleanly cancelled rather than blocking until completion regardless of a cancellation request.
async Task CopyLargeFileAsync(string source, string dest, CancellationToken token) {
using FileStream sourceStream = File.OpenRead(source);
using FileStream destStream = File.Create(dest);
await sourceStream.CopyToAsync(destStream, token); // respects cancellation mid-copy
}
Real-world example
Allowing a user to cancel a large file upload or copy operation mid-way through, rather than forcing them to wait for it to finish.
Common follow-ups: What happens to a partially-written destination file if the copy operation is cancelled midway?
Asynchronous Programming
How would you implement a robust, atomic file-write operation that avoids leaving a corrupted or partially-written file if the process crashes mid-write?
Advanced
Write the new content to a TEMPORARY file first, then use File.Replace() (or File.Move() with overwrite) to atomically swap it into place as the final destination — since the OS-level rename/replace operation is atomic, readers never see a partially-written file, unlike directly overwriting the original file in place.
async Task SafeWriteAsync(string path, string content) {
string tempPath = path + ".tmp";
await File.WriteAllTextAsync(tempPath, content);
File.Move(tempPath, path, overwrite: true); // atomic swap -- no partial-write window
}
Real-world example
Safely updating a critical configuration or data file that other processes might read concurrently, without ever exposing a half-written state.
Common follow-ups: Why is directly calling File.WriteAllTextAsync() on the FINAL destination path risky for a process that might crash mid-write?
Exception Handling