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
58: Extension Methods
How do I actually "add" a method to a class I didn't write?
It feels like magic, but you aren't actually modifying the original source code of the class. Instead, you're creating a static method in a static class, and you're telling the C# compiler to "pretend" it's an instance method. The secret sauce is the this keyword in the first parameter of your method.
Let's say you're building a blog and you constantly need to turn titles into URL-friendly "slugs" (like turning "Hello World!" into "hello-world"). Rather than passing a string into a helper method every time, you can just extend string itself:
public static class StringExtensions
{
public static string ToSlug(this string text)
{
if (string.IsNullOrWhiteSpace(text)) return string.Empty;
return text.ToLower()
.Replace(" ", "-")
.TrimStart('-')
.TrimEnd('-');
}
}
Now, anywhere in your project, you can just call myTitle.ToSlug(). I love this because it keeps the calling code clean. You don't have to go hunting for a StringHelper class; the method just shows up in IntelliSense as if it were always there.
Isn't this just a fancy wrapper for a utility class?
Technically, yes. Under the hood, the compiler is just converting your call to a standard static method call. But from a developer experience perspective, it's a huge win. When you use a utility class, you have to remember the name of the class (Is it FileUtils? FileHelper? SystemUtils?). With extension methods, the functionality is discovered through the object you're already working with.
I've seen a lot of devs overdo this, though. Don't extend object or string with a hundred niche methods that only apply to one specific corner of your app. If a method is only useful in one specific service, just keep it in that service. Extension methods are best for general-purpose logic that feels like it should have been part of the original type.
Can I do this with generic collections too?
Absolutely. In fact, this is exactly how LINQ (Language Integrated Query) works. Every single LINQ method like .Where() or .Select() is just an extension method on IEnumerable<T>. You can do the same thing for your own custom logic.
For example, I often find myself needing to grab a random element from a list. C# doesn't have a built-in .Random() method for lists, so I just write my own:
public static class CollectionExtensions
{
private static readonly Random _random = new Random();
public static T GetRandomElement<T>(this IList<T> list)
{
if (list == null || list.Count == 0)
throw new InvalidOperationException("The list is empty.");
int index = _random.Next(list.Count);
return list[index];
}
}
Now, whether you have a List<string> or a List<User>, you can just call myList.GetRandomElement(). It's clean, reusable, and typesafe.
📋 Practical Task
Exercise: Building a Currency Formatter Extension
In many financial applications, you need to format decimal values consistently. Your task is to create an extension method for the decimal type that formats a number as a currency string with a specific prefix and two decimal places, but only if the value is positive. If the value is negative, it should return the string "N/A".
Requirements:
- Create a static class named
CurrencyExtensions. - Implement a method called
ToPriceStringthat takes adecimalas the target (using thethiskeyword). - The method should take a second parameter:
string currencySymbol(e.g., "$", "€"). - If the value is 12.5, and the symbol is "$", it should return "$12.50".
- If the value is -1.0, it should return "N/A".
Test your code with these cases:
decimal price1 = 49.99m;
Console.WriteLine(price1.ToPriceString("$")); // Expected: $49.99
decimal price2 = -5.00m;
Console.WriteLine(price2.ToPriceString("$")); // Expected: N/A
decimal price3 = 10m;
Console.WriteLine(price3.ToPriceString("£")); // Expected: £10.00There are no comments for now.