Delegates, Func and Action
Treat methods as values: store them in variables, pass them to other methods, and combine them. The foundation of lambdas, events and LINQ.
A delegate type
// a delegate type: any method taking a double and returning a doublepublic delegate double Operation(double x); static double Square(double x) => x * x;static double Half(double x) => x / 2; Operation op = Square; // store a method (no brackets: we don't call it)Console.WriteLine(op(5)); // call it through the variable op = Half; // swap the behaviourConsole.WriteLine(op(5));25 2.5
Func and Action: ready-made delegate types
| Type | Represents | Example |
|---|---|---|
Action | Action save = SaveProject; | |
Action<T1, T2…> | Action<string> log = Console.WriteLine; | |
Func<TResult> | Func<DateTime> now = () => DateTime.Now; | |
Func<T1…, TResult> | Func<double, double> op = Math.Sqrt; | |
Predicate<T> | Predicate<int> isEven = n => n % 2 == 0; |
Passing behaviour to a method
static double[] Transform(double[] values, Func<double, double> op){ var result = new double[values.Length]; for (int i = 0; i < values.Length; i++) result[i] = op(values[i]); // call whatever method we were given return result;} double[] loads = { 10, 25, 40 };Console.WriteLine(string.Join(", ", Transform(loads, Math.Sqrt)));Console.WriteLine(string.Join(", ", Transform(loads, x => x * 1.5))); // a lambda3.1622776601683795, 5, 6.324555320336759 15, 37.5, 60
Transform doesn’t know or care what the operation is. The caller decides. The x => x * 1.5 is a lambda: a small unnamed method written inline (next lesson).
Multicast delegates
Action<string> toScreen = msg => Console.WriteLine($"[screen] {msg}");Action<string> toLog = msg => Console.WriteLine($"[log] {msg}"); Action<string>? notify = toScreen;notify += toLog; // now holds two methodsnotify("Model saved"); // both run, in order notify -= toLog; // remove onenotify("Exported");[screen] Model saved [log] Model saved [screen] Exported
+= adds one, -= removes one, and invoking it calls them all in order. This is exactly how events work (a later lesson).
Where you will see delegates
list.Sort((a, b) => a.Length.CompareTo(b.Length)): how to compare.list.Find(b => b.Mark == "B3"),list.RemoveAll(…): which items match.LINQ: beams.Where(b => b.LengthM > 6).Select(b => b.Mark).Button clicks and other events in WPF and WinForms. Task.Run(() => HeavyWork()): what to run in the background.
static int CountWhere(int[] numbers, Func<int, bool> test) that counts how many numbers pass the test. Use it to count even numbers and numbers greater than 50 in { 12, 75, 40, 51, 8, 99 }.
Show the solution
static int CountWhere(int[] numbers, Func<int, bool> test){ int count = 0; foreach (int n in numbers) if (test(n)) count++; return count;} int[] data = { 12, 75, 40, 51, 8, 99 };Console.WriteLine(CountWhere(data, n => n % 2 == 0));Console.WriteLine(CountWhere(data, n => n > 50));3 3
Remember
A delegate is a type-safe reference to a method. Use Func<…, TResult>for methods that return,Action<…>for void methods.Passing delegates lets the caller decide behaviour (sorting, filtering, transforming). Delegates are multicast ( +=/-=); invoke with?.Invoke().