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
205: Encoding Classes: UTF8, ASCII, Unicode
I can't tell you how many times I've walked into a codebase only to find a "bug" that was actually just a character encoding mismatch. You'll see it in the logs first: a name like "Müller" suddenly becomes "Müller" or, worse, "Mller". It's frustrating because the code looks perfectly fine, but the data is being mangled during the transition from a string in memory to a sequence of bytes on a disk or a network socket.
The "It Works on My Machine" Trap
When you first start working with bytes in C#, the temptation is to reach for Encoding.ASCII because it's simple. If you're just sending basic alphanumeric commands to a legacy piece of hardware, it works great. But the moment you hit a character outside the standard 127-character ASCII set, ASCII doesn't "fail" by throwing an exception—it just replaces the unknown character with a question mark. I've seen entire database migrations ruined because someone used ASCII to save a list of global customer names.
// The naive way: assuming the world is English-only
string customerName = "Sato 佐藤";
byte[] data = Encoding.ASCII.GetBytes(customerName);
string result = Encoding.ASCII.GetString(data);
Console.WriteLine(result); // Output: Sato ??
You might think, "I'll just use Encoding.Default then." Please, don't. Encoding.Default is a gamble. On an older Windows machine in the US, it might be one thing; on a server in Japan, it's another. Relying on the system's default encoding is a recipe for "it works on my machine" bugs that only appear once the code is deployed to a production server in a different region.
Why UTF-8 is the Industry Standard
In almost every professional scenario you'll encounter, Encoding.UTF8 is the correct answer. The beauty of UTF-8 is that it's backward compatible with ASCII—any valid ASCII file is also a valid UTF-8 file—but it can scale up to represent every single character in the Unicode standard. It does this using a variable-width encoding: common English characters take 1 byte, while more complex characters take more. It's the perfect balance between space efficiency and universal compatibility.
// The professional way: Explicitly using UTF-8
string customerName = "Sato 佐藤";
byte[] data = Encoding.UTF8.GetBytes(customerName);
string result = Encoding.UTF8.GetString(data);
Console.WriteLine(result); // Output: Sato 佐藤
When you're writing files or calling an API, always be explicit. Don't let the framework guess. By specifying Encoding.UTF8, you're telling the next developer (and the operating system) exactly how to interpret those bytes, regardless of where the code is running.
The Internal Secret of C# Strings
Here is a bit of a curveball: while we use UTF-8 for storage and transmission, C# doesn't use UTF-8 internally. In .NET, the string type is actually encoded as UTF-16 (which is what Encoding.Unicode refers to). This means every character in a C# string generally takes 2 bytes.
You don't usually need to worry about this because the Encoding classes handle the translation for you. However, it's important to understand the trade-off. UTF-16 is faster for the CPU to index into (since most characters are a fixed size), but it's bloated for storage. That's why we use Encoding.Unicode when we need to match a specific Windows API requirement, but we stick to Encoding.UTF8 for everything else. If you're ever confused about which one to use for a web request or a JSON file, just remember: the internet runs on UTF-8.
📋 Practical Task
Exercise: Fixing the Corrupted User Profile Loader
You have been handed a legacy piece of code that loads user profiles from a text file. The profiles contain international names, but the previous developer used Encoding.ASCII, resulting in corrupted data. Your task is to fix the loader so it correctly handles Unicode characters.
Requirements:
- Create a file named
profiles.txtusingFile.WriteAllText, ensuring you explicitly save it as UTF-8. The file should contain a string with diverse characters, such as:"User: André, Location: Montréal, Status: 🚀". - Write a method
LoadProfile()that reads this file. - First, implement the read using
Encoding.ASCIIand print the result to the console to observe the corruption. - Then, refactor the method to use
Encoding.UTF8to correctly retrieve the original string. - Verify that the output matches the original input exactly.
There are no comments for now.