Roadmap › C# Language — Basics to Advanced › Module: Methods and Functional C#
Lessons in this course (38)
23. Anonymous methods, Predicate and multicast delegates
IntermediateMediumLesson 23 of 38
From named method → anonymous method → lambda (the same delegate, three syntaxes through C# history):
csharp
public delegate int Calc(int a, int b); // custom delegate type: the method "signature"
static int Add(int a, int b) => a + b;
Calc c1 = Add; // C# 1: named method
Calc c2 = delegate (int a, int b) { return a * b; }; // C# 2: anonymous method
Calc c3 = (a, b) => a - b; // C# 3: lambda expression
Console.WriteLine(c1(4, 2) + c2(4, 2) + c3(4, 2)); // 6 + 8 + 2 = 16Anonymous methods can omit the parameter list (delegate { ... }) when you ignore the parameters; otherwise lambdas replaced them.
Built-in generic delegates:
| Delegate | Signature | Example |
|---|---|---|
Func<T1, ..., TResult> | Up to 16 inputs, returns TResult | Func<decimal, decimal> tax = p => p * 0.18m; |
Action<T1, ...> | Up to 16 inputs, returns void | Action<string> log = Console.WriteLine; |
Predicate<T> | One input, returns bool | Predicate<int> isAdult = age => age >= 18; |
csharp
var ages = new List<int> { 12, 25, 17, 40 };
Predicate<int> isAdult = a => a >= 18;
List<int> adults = ages.FindAll(isAdult); // List<T>.FindAll/Find/RemoveAll take Predicate<T>
Func<int, bool> sameIdea = a => a >= 18; // LINQ uses Func<T,bool>: ages.Where(sameIdea)Multicast delegates: one delegate instance can hold an invocation list of several methods.
csharp
static void Email(string msg) => Console.WriteLine($"Email: {msg}");
static void Sms(string msg) => Console.WriteLine($"SMS: {msg}");
Action<string> notify = Email;
notify += Sms;
notify += msg => Console.WriteLine($"Push: {msg}");
notify("Order shipped"); // all three run, in order
notify -= Sms; // remove works with the same method; a lambda can't be removed unless you kept a reference to it
notify("Delivered"); // Email + Push
Func<int> f = () => 1;
f += () => 2;
Console.WriteLine(f()); // 2 — only the LAST return value is keptIf one handler throws, the remaining handlers don't run — loop over GetInvocationList() and wrap each call in try/catch when you need isolation.