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 = 16

Anonymous methods can omit the parameter list (delegate { ... }) when you ignore the parameters; otherwise lambdas replaced them.

Built-in generic delegates:

DelegateSignatureExample
Func<T1, ..., TResult>Up to 16 inputs, returns TResultFunc<decimal, decimal> tax = p => p * 0.18m;
Action<T1, ...>Up to 16 inputs, returns voidAction<string> log = Console.WriteLine;
Predicate<T>One input, returns boolPredicate<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 kept

If one handler throws, the remaining handlers don't run — loop over GetInvocationList() and wrap each call in try/catch when you need isolation.