<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. C#
  3. Functional C# and LINQ
All lessons in this course

Lesson 37 of 60 14 minIntermediate

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.

So far, data went into methods and methods were fixed. A delegate is a type whose values are methods. With it you can pass behaviour around: “here is a list, and here is what to do with each item”. This idea powers events, LINQ and most modern C#.

A delegate type

Delegate.cs
// 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));
Output
25
2.5

Func and Action: ready-made delegate types

You rarely declare your own delegate types. .NET provides generic ones:

TypeRepresentsExample
Actionno parameters, no resultAction save = SaveProject;
Action<T1, T2…>parameters, no resultAction<string> log = Console.WriteLine;
Func<TResult>no parameters, returns TResultFunc<DateTime> now = () => DateTime.Now;
Func<T1…, TResult>parameters, last type is the resultFunc<double, double> op = Math.Sqrt;
Predicate<T>a test returning boolPredicate<int> isEven = n => n % 2 == 0;

Passing behaviour to a method

Interactive

Methods as values

Func<double, double> op = …;
39
10100
2.56.25
-416
double[] result = Transform(values, Square); // Transform calls op(x) for every x
Action onSaved = null; (multicast: += adds, -= removes)
Invocation list:SaveToDisk()WriteLog()
A multicast delegate calls every method in its list, in the order they were added.
Transform.cs
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 lambda
Output
3.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

Multicast.cs
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");
Output
[screen] Model saved
[log] Model saved
[screen] Exported

A delegate can hold a list of methods: += 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.
Quick check

Which delegate type matches a method bool IsHeavy(Beam b)?

Quick check

What is the difference between op = Square; and op = Square(3);?

Practice

Write 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
Solution.cs
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));
Output
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().
0 of 60 lessons completed