C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
202: Path Manipulation Utilities
I once worked with a junior developer who spent an entire Friday afternoon chasing a "File Not Found" exception that only happened in our staging environment. On his local Windows machine, everything worked perfectly. But the moment the code hit our Linux-based containers in the cloud, it fell apart. He had been building file paths using string interpolation: $"{folder}\\{fileName}". He'd hard-coded the backslash, assuming it was a universal constant. In the Linux world, the backslash is just another character in a filename, not a directory separator. He didn't realize he'd built a Windows-only app until the build pipeline turned red.
This is why we never, ever treat file paths as simple strings. Whether you're dealing with different operating systems or just trying to avoid the headache of missing trailing slashes, C# provides the System.IO.Path class. It's a set of static utilities designed to handle the messy reality of file systems so you don't have to write regex patterns or manually check for slashes.
Stop Concatenating and Start Combining
The most important tool in this kit is Path.Combine(). Instead of guessing whether your folder path ends with a slash, you pass the individual parts of the path into this method, and .NET handles the glue. It uses the correct Path.DirectorySeparatorChar for the environment the code is currently running on.
string root = @"C:\AppData\Logs";
string subFolder = "October";
string fileName = "error.log";
// Do this:
string fullPath = Path.Combine(root, subFolder, fileName);
// Result on Windows: C:\AppData\Logs\October\error.log
// Result on Linux: C:\AppData\Logs/October/error.log (if root was adjusted)
One thing to watch out for: if one of the arguments passed to Combine is an absolute path (like starting with C:\ or /), Combine assumes you want to start over from that root and will discard everything that came before it. It's a quirk that has tripped up many of us, but it's actually quite useful when you need to override a default directory.
Slicing Paths Without String Splitting
When you need to know the extension of a file or the name of the folder containing it, resist the urge to use string.Split('/'). File paths are inconsistent; some have trailing slashes, some don't, and some use different separators entirely. The Path class has dedicated methods for these operations that are far more robust.
string filePath = @"C:\Users\Dev\Documents\report.pdf";
string extension = Path.GetExtension(filePath); // ".pdf"
string fileName = Path.GetFileName(filePath); // "report.pdf"
string nameOnly = Path.GetFileNameWithoutExtension(filePath); // "report"
string directory = Path.GetDirectoryName(filePath); // "C:\Users\Dev\Documents"
I personally find GetFileNameWithoutExtension to be a lifesaver when implementing "Save As" logic or generating thumbnails, where you need the base name of the file but want to append a new suffix like _thumb.jpg.
Resolving Relative Paths and Normalization
Sometimes you'll encounter paths that contain "dots"—like .\config\settings.json or ..\..\logs\app.log. These are relative paths, and while they make sense to the OS, they can be a nightmare to track in your application logs or when comparing two paths for equality.
Path.GetFullPath() takes a relative path and resolves it into a fully qualified absolute path based on the current working directory. This "normalizes" the path, removing the .. segments and giving you a clean, unambiguous string. It's a great way to sanitize user input before you attempt to open a file, ensuring you know exactly where on the disk the application is looking.
📋 Practical Task
Build a Log File Archive Path Resolver
You are building a system that archives logs. Your task is to create a method that takes a base archive directory, a relative sub-folder (representing the year/month), and a raw filename. The method must return a normalized, absolute path to the file, but only if the file extension is .log or .txt. If the extension is invalid, it should throw an InvalidOperationException.
Requirements:
- Use
Path.Combineto merge the base directory, sub-folder, and filename. - Use
Path.GetExtensionto validate the file type. - Use
Path.GetFullPathto ensure the final result is an absolute path. - Handle the case where the sub-folder might be provided as a relative path (e.g.,
"../2023/October").
public string ResolveArchivePath(string baseDir, string subFolder, string fileName)
{
// Your implementation here
}There are no comments for now.