Func, Action și Predicate în C# (.NET)

Lambda, delegați și metode de extensie .NET Framework 3.5 / C# 3.0

Tipurile de delegat gata făcute din .NET: Func returnează o valoare, Action nu returnează nimic, Predicate returnează bool.

Sintaxă C#
Func<int, int, int> f = (a, b) => a + b;   Action<string> a = s => Console.WriteLine(s);   Predicate<int> p = n => n > 0;

Ce face Func, Action și Predicate

Func<T1, ..., TResult> reprezintă o funcție cu până la 16 parametri, iar ultimul argument generic este tipul returnat: Func<string, int> primește un string și dă un int. Action<T1, ...> este la fel, dar fără valoare returnată; Action simplu nu are parametri.

Predicate<T> este echivalent cu Func<T, bool> și apare în metode mai vechi precum List<T>.FindAll și RemoveAll. Cele două tipuri nu sunt interschimbabile direct, deși au aceeași formă.

Primind un Func sau un Action ca parametru, o metodă își lasă o parte din comportament la alegerea celui care o apelează, fără să declari un tip de delegat propriu.

Parametri

Parametru Descriere
Func&lt;TResult&gt; Fără parametri, returnează TResult.
Func&lt;T, TResult&gt; Un parametru; ultimul tip generic este mereu cel returnat.
Action&lt;T&gt; Un parametru, fără valoare returnată.
Predicate&lt;T&gt; Un parametru, returnează bool.

Exemple

Exemplul 1: Metodă care primește comportamentul ca parametru

List<int> preturi = [120, 45, 300, 80];

Console.WriteLine(Aplica(preturi, p => p * 2));
Console.WriteLine(Aplica(preturi, p => p - p / 10));

Action<string> jurnal = mesaj => Console.WriteLine($"[log] {mesaj}");
jurnal("Calcul terminat");

static string Aplica(List<int> valori, Func<int, int> transformare)
{
    var rezultat = new List<int>();
    foreach (int v in valori)
        rezultat.Add(transformare(v));
    return string.Join(" ", rezultat);
}

Rezultat

240 90 600 160
108 41 270 72
[log] Calcul terminat

Exemplul 2: Predicate cu FindAll și RemoveAll

List<string> orase = ["Iași", "Arad", "Cluj", "Alba Iulia"];

Predicate<string> incepeCuA = o => o.StartsWith('A');
Console.WriteLine(string.Join(", ", orase.FindAll(incepeCuA)));

int sterse = orase.RemoveAll(incepeCuA);
Console.WriteLine($"{sterse} șterse, rămân: {string.Join(", ", orase)}");

Func<string> ora = () => "12:00";
Console.WriteLine(ora());

Rezultat

Arad, Alba Iulia
2 șterse, rămân: Iași, Cluj
12:00

Greșeli frecvente

  • Inversarea ordinii la Func: în Func<int, string> parametrul este int, iar rezultatul string.
  • Apelarea unui delegat null aruncă NullReferenceException. Pentru parametri opționali folosește callback?.Invoke(x).
  • Un Predicate<T> nu se poate da direct unei metode LINQ care cere Func<T, bool>; scrie lambda din nou sau folosește p.Invoke ca grup de metode.

Comenzi înrudite

  • Expresii lambda (=>): O funcție scurtă, fără nume, scrisă pe loc: parametri => rezultat. Se folosește în LINQ, la evenimente și oriunde se cere un delegat.
  • delegate: Un delegat este un tip care ține o referință spre o metodă cu o anumită semnătură; poate ține mai multe metode (multicast).
  • event și EventHandler: Un eveniment anunță abonații când se întâmplă ceva; din afara clasei te poți doar abona (+=) sau dezabona (-=).