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
49: Func, Action, and Predicate Delegates
A few years ago, I was reviewing a PR for a junior dev who was building a report generator. He had created about fifteen different methods that all looked almost identical: FilterOrdersByDate, FilterOrdersByCustomer, FilterOrdersByAmount, and so on. Each method took a list of orders and returned a filtered list. It was a maintenance nightmare. Every time a stakeholder wanted a new filter, he had to write a whole new method, compile, and deploy. I told him, "Stop writing methods that only differ by one line of logic. Just pass the logic itself as a parameter."
That's where these built-in delegate types come in. You already know how to declare a custom delegate, but in modern C#, we almost never do that manually. It's too much boilerplate. Microsoft gave us Action, Func, and Predicate to handle 99% of our needs. Think of them as generic templates for function pointers.
The Three Workhorses of Logic Passing
First, let's look at Action. An Action is for when you want to do something, but you don't need a value back. It's a "fire and forget" delegate. It returns void. You can have an Action with no parameters, or one that takes up to 16 different arguments. I use these all the time for logging or updating a UI progress bar where the method performing the work doesn't care what happens after the update is sent.
// An Action that takes a string and returns void
Action<string> logger = message => Console.WriteLine($"[LOG]: {message}");
logger("System started");
Then we have Func. This is the opposite of Action; it must return a value. The magic of Func is in the generic parameters. The last type listed is always the return type. So, Func<int, int, string> takes two integers and returns a string. If you see a Func, think "calculation" or "transformation."
// Takes two ints, returns an int
Func<int, int, int> add = (a, b) => a + b;
int result = add(5, 10); // 15
Finally, there's Predicate<T>. Now, technically, a Predicate<T> is just a specialized version of Func<T, bool>. It takes one object and returns a boolean. Why does it exist separately? Mostly for semantic clarity. When you see Predicate in a method signature, you know instantly that the method is testing whether an object meets a certain condition. It's the backbone of most filtering logic in .NET.
Integrating Delegates into Your Classes
The real power happens when you stop using these as local variables and start using them as parameters for your methods. This allows you to write "Higher-Order Functions"—methods that don't know how they'll be processing data, only that they will process it using the logic you provide.
Imagine a DiscountEngine. Instead of hard-coding a "Black Friday" or "Clearance" discount, you can just let the caller pass in the math.
public class DiscountEngine
{
public decimal ApplyDiscount(decimal price, Func<decimal, decimal> discountStrategy)
{
return discountStrategy(price);
}
}
// Usage
var engine = new DiscountEngine();
// Pass a 10% discount logic on the fly
var tenPercent = engine.ApplyDiscount(100m, p => p * 0.9m);
// Pass a flat $20 off logic
var flatDiscount = engine.ApplyDiscount(100m, p => p - 20m);
By using Func here, the DiscountEngine is now completely decoupled from the business rules. You can add a thousand different discount types without ever touching the DiscountEngine class again. That's how you avoid the "method explosion" I mentioned earlier.
📋 Practical Task
Build a Dynamic Inventory Filter and Notifier
You are tasked with creating a simple inventory management utility. You need to build a system that can filter a list of products based on a dynamic condition and then execute a custom action for every product that matches that condition.
Requirements:
- Create a
Productclass with properties forName(string),Price(decimal), andStockQuantity(int). - Create an
InventoryManagerclass with a method calledProcessProducts. - The
ProcessProductsmethod must take three parameters:- A
List<Product>. - A
Predicate<Product>to determine which products to process. - An
Action<Product>to define what to do with each product that passes the filter.
- A
- In your
Mainmethod, instantiate a list of at least 5 products. - Call
ProcessProductstwice:- First, filter for products with
StockQuantityless than 5 and use theActionto print "Restock needed for: [Name]". - Second, filter for products with
Pricegreater than 100 and use theActionto print "Premium item found: [Name]".
- First, filter for products with
There are no comments for now.