Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Delegate Shapes: Method Groups, Func, Action, Predicate

โญโญ intermediateโณ 16 min read๐Ÿ“ Lesson 94 of 265

The type behind every lambda, why method groups convert, and which generic shape fits which job.

A delegate type is a function's type

delegate int Calculator(int a, int b) declares that some methods have this shape. Any method matching the signature converts implicitly โ€” the method group conversion:

static int Add(int a, int b) => a + b;
Calculator op = Add;          // method group -> delegate instance
int result = op(2, 3);        // invoke like a method

You will rarely declare one. The BCL ships the shapes you need:

  • Action / Action<T...> โ€” takes 0..16 args, returns void.
  • Func<T... TResult> โ€” takes 0..16 args, returns TResult.
  • Predicate<T> โ€” takes T, returns bool. (Func<T, bool> in a trench coat; both work with LINQ, Predicate reads better on List<T>.Find.)
Action tick = () => Console.WriteLine("tick");
Func<int, int> square = x => x * x;
Func<int, int, int> add = static (a, b) => a + b;   // static: no capture allowed
Predicate<string> isEmpty = string.IsNullOrEmpty;

static lambdas promise no capture โ€” a compiler-checked guard against accidentally closing over state.

Multicast: delegates are lists in disguise

+ and - compose delegate instances into an invocation list:

Action pipeline = Step1;
pipeline += Step2;        // now both run, in order
pipeline -= Step1;        // only Step2 remains

Invocation returns the LAST handler's value (for Func) โ€” a reason multicast fits Actions (events) far better than Funcs.

Check your understanding

  • Why does Func<T, bool> coexist with Predicate<T>? (History; List<T> predates Func.)
  • What does += on a delegate actually do? (Creates a new delegate whose list is both.)