Delegáty a výrazy lambda

Delegát definuje typ, který představuje odkazy na metody, které mají konkrétní seznam parametrů a návratový typ. Metoda (statická nebo instance), jejíž seznam parametrů a shoda návratového typu mohou být přiřazeny proměnné tohoto typu, pak volané přímo (s příslušnými argumenty) nebo předány jako samotný argument jiné metodě a pak volané. Následující příklad ukazuje použití delegáta.

using System;
using System.Linq;

public class Program
{
    public delegate string Reverse(string s);

    static string ReverseString(string s)
    {
        return new string(s.Reverse().ToArray());
    }

    static void Main(string[] args)
    {
        Reverse rev = ReverseString;

        Console.WriteLine(rev("a string"));
    }
}
  • Řádek public delegate string Reverse(string s); vytvoří typ delegáta metody, která přebírá řetězcový parametr a poté vrátí řetězcový parametr.
  • Metoda static string ReverseString(string s) , která má úplně stejný seznam parametrů a návratový typ jako definovaný typ delegáta, implementuje delegáta.
  • Řádek Reverse rev = ReverseString; ukazuje, že můžete přiřadit metodu proměnné odpovídajícího typu delegáta.
  • Řádek Console.WriteLine(rev("a string")); ukazuje, jak použít proměnnou typu delegáta k vyvolání delegáta.

Aby bylo možné zjednodušit proces vývoje, obsahuje .NET sadu typů delegátů, které programátoři můžou opakovaně používat a nemusí vytvářet nové typy. Tyto typy jsou Func<>Action<> a Predicate<>lze je použít bez nutnosti definovat nové typy delegátů. Existují určité rozdíly mezi třemi typy, které se musí shodovat s tím, jak byly určeny k použití:

  • Action<> se používá, pokud je potřeba provést akci pomocí argumentů delegáta. Metoda zapouzdřuje nevrací hodnotu.
  • Func<> se používá obvykle v případě, že máte na ruce transformaci, to znamená, že potřebujete transformovat argumenty delegáta na jiný výsledek. Dobrým příkladem jsou projekce. Metoda, kterou zapouzdřuje, vrátí zadanou hodnotu.
  • Predicate<> se používá, pokud potřebujete určit, zda argument splňuje podmínku delegáta. Může být také zapsán jako Func<T, bool>, což znamená, že metoda vrací logickou hodnotu.

Teď můžeme použít výše uvedený příklad a přepsat ho pomocí delegáta Func<> místo vlastního typu. Program bude dál běžet úplně stejně.

using System;
using System.Linq;

public class Program
{
    static string ReverseString(string s)
    {
        return new string(s.Reverse().ToArray());
    }

    static void Main(string[] args)
    {
        Func<string, string> rev = ReverseString;

        Console.WriteLine(rev("a string"));
    }
}

V tomto jednoduchém příkladu se zdá, že metoda definovaná mimo metodu Main je trochu nadbytečná. Rozhraní .NET Framework 2.0 zavedlo koncept anonymních delegátů, které umožňují vytvářet vložené delegáty, aniž byste museli zadávat žádný další typ nebo metodu.

V následujícím příkladu anonymní delegát filtruje seznam jenom na sudá čísla a pak je vytiskne do konzoly.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();

        for (int i = 1; i <= 100; i++)
        {
            list.Add(i);
        }

        List<int> result = list.FindAll(
          delegate (int no)
          {
              return (no % 2 == 0);
          }
        );

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

Jak vidíte, tělo delegáta je jen sada výrazů, stejně jako jakýkoli jiný delegát. Místo samostatné definice jsme ji ale v našem volání List<T>.FindAll metody zavedli ad hoc.

I s tímto přístupem ale stále existuje mnoho kódu, který můžeme vyhodit. Tady se přehrávají výrazy lambda. Výrazy lambda (neboli jen výrazy lambda) byly ve verzi C# 3.0 zavedeny jako jeden ze základních stavebních bloků jazyka LINQ (Language Integrated Query). Jsou to jen pohodlnější syntaxe pro použití delegátů. Deklarují seznam parametrů a tělo metody, ale nemají vlastní formální identitu, pokud nejsou přiřazeni delegátovi. Na rozdíl od delegátů je možné je přímo přiřadit jako pravou stranu registrace události nebo v různých klauzulích a metodách LINQ.

Vzhledem k tomu, že výraz lambda je jen dalším způsobem určení delegáta, měli bychom být schopni přepsat výše uvedenou ukázku tak, aby místo anonymního delegáta používal výraz lambda.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();

        for (int i = 1; i <= 100; i++)
        {
            list.Add(i);
        }

        List<int> result = list.FindAll(i => i % 2 == 0);

        foreach (var item in result)
        {
            Console.WriteLine(item);
        }
    }
}

V předchozím příkladu je i => i % 2 == 0použitý výraz lambda . Opět se jedná o pohodlnou syntaxi pro použití delegátů. Co se stane pod kryty, je podobné tomu, co se stane s anonymním delegátem.

Lambda jsou opět jen delegáti, což znamená, že je možné je použít jako obslužnou rutinu události bez jakýchkoli problémů, jak ukazuje následující fragment kódu.

public MainWindow()
{
    InitializeComponent();

    Loaded += (o, e) =>
    {
        this.Title = "Loaded";
    };
}

Operátor += v tomto kontextu slouží k přihlášení k odběru události. Další informace najdete v tématu Jak se přihlásit k odběru a odhlásit odběr událostí.

Další čtení a zdroje informací