Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
K vytvoření anonymní funkce použijte výraz lambda . Pomocí operátoru deklarace lambda => oddělte seznam parametrů lambda od jejího těla.
Referenční dokumentace jazyka C# dokumentuje naposledy vydané verze jazyka C#. Obsahuje také počáteční dokumentaci k funkcím ve verzi Public Preview pro nadcházející jazykovou verzi.
Dokumentace identifikuje všechny funkce, které byly poprvé představeny v posledních třech verzích jazyka nebo v aktuálních verzích Public Preview.
Návod
Informace o tom, kdy byla funkce poprvé představena v jazyce C#, najdete v článku o historii verzí jazyka C#.
Výraz lambda může být libovolný z následujících dvou forem:
výraz lambda, který má výraz jako text:
(input-parameters) => expressionvýraz lambda, který má blok příkazu jako jeho text:
(input-parameters) => { <sequence-of-statements> }
Pokud chcete vytvořit výraz lambda, zadejte vstupní parametry (pokud existuje) na levé straně operátoru lambda a výraz nebo blok příkazu na druhé straně.
Libovolný výraz lambda můžete převést na typ delegáta . Typy parametrů a návratová hodnota definují typ delegáta, na který lze výraz lambda převést. Pokud výraz lambda nevrací hodnotu, převeďte ho na jeden z typů delegátů Action . Pokud vrátí hodnotu, převeďte ji na jeden z typů delegátů Func . Například převeďte výraz lambda, který má dva parametry a nevrací žádnou hodnotu delegátovi Action<T1,T2> . Převede výraz lambda, který má jeden parametr a vrátí hodnotu delegátovi Func<T,TResult> . V následujícím příkladu je výraz lambda x => x * x, který určuje parametr s názvem x a vrací hodnotu x čtverce, je přiřazena proměnné typu delegáta:
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25
Výrazy lambda můžete také převést na typy stromu výrazů , jak ukazuje následující příklad:
System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x;
Console.WriteLine(e);
// Output:
// x => (x * x)
Výrazy lambda používejte v libovolném kódu, který vyžaduje instance typů delegátů nebo stromů výrazů. Jedním z příkladů je argument metody Task.Run(Action) pro předání kódu, který by měl být proveden na pozadí. Výrazy lambda můžete použít také při psaní LINQ v jazyce C#, jak ukazuje následující příklad:
int[] numbers = { 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(x => x * x);
Console.WriteLine(string.Join(" ", squaredNumbers));
// Output:
// 4 9 16 25
Pokud používáte syntaxi založenou na metodách k volání metody Enumerable.Select ve třídě System.Linq.Enumerable, například v LINQ to Objects a LINQ to XML, je parametr typu delegáta System.Func<T,TResult>. Když voláte metodu Queryable.Select ve třídě System.Linq.Queryable, typ parametru je strom výrazu Expression<Func<TSource,TResult>>, například v LINQ to SQL. V obou případech můžete k zadání hodnoty parametru použít stejný výraz lambda. To způsobuje, že dvě Select volání vypadají podobně, ačkoli ve skutečnosti je typ objektů, které jsou vytvořeny z lambd, odlišný.
Výrazy lambda
Výraz lambda s výrazem na pravé straně operátoru => se nazývá výraz lambda . Výraz lambda vrátí výsledek výrazu a má následující základní formu:
(input-parameters) => expression
Tělo výrazu lambda se může skládat z volání metody. Když ale vytvoříte stromy výrazů , které poskytovatel dotazu vyhodnotí, omezte volání metody na metody, které poskytovatel dotazu přeloží do jeho formátu. Různí poskytovatelé dotazů mají různé možnosti. Mnoho zprostředkovatelů založených na SQL může například překládat metody jako String.StartsWith do vhodných výrazů SQL, jako LIKEje . Pokud poskytovatel dotazů nerozpozná volání metody, nemůže výraz přeložit ani spustit.
Výroková lambda
Příkaz lambda se podobá výrazu lambda, ale jeho příkazy jsou uzavřeny ve složených závorkách:
(input-parameters) => { <sequence-of-statements> }
Tělo výrazu lambda se může skládat z libovolného počtu příkazů. V praxi však obvykle neexistují více než dvě nebo tři.
Action<string> greet = name =>
{
string greeting = $"Hello {name}!";
Console.WriteLine(greeting);
};
greet("World");
// Output:
// Hello World!
Výrazy lambda nemůžete použít k vytvoření stromů výrazů.
Vstupní parametry výrazu lambda
Uzavře vstupní parametry výrazu lambda do závorek. Zadejte nulové vstupní parametry pomocí prázdných závorek:
Action line = () => Console.WriteLine();
Pokud výraz lambda obsahuje pouze jeden vstupní parametr, můžete vynechat závorky:
Func<double, double> cube = x => x * x * x;
Dva nebo více vstupních parametrů oddělte čárkami:
Func<int, int, bool> testForEquality = (x, y) => x == y;
Kompilátor obvykle odvodí typy parametrů na výrazy lambda, který se nazývá implicitně zadaný seznam parametrů. Typy můžete zadat explicitně, což se nazývá explicitně zadaný seznam parametrů. Následující příklad ukazuje explicitně zadaný seznam parametrů:
Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;
Vstupní typy parametrů musí být explicitní nebo všechny implicitní. V opačném případě dojde k chybě kompilátoru CS0748 . Před jazykem C# 14 je nutné do parametru zahrnout explicitní typ, pokud má nějaké modifikátory, například ref nebo out. V jazyce C# 14 se toto omezení odebere. Nicméně, musíte deklarovat typ, pokud použijete params modifikátor.
Pomocí zahození zadejte dva nebo více vstupních parametrů výrazu lambda, který se ve výrazu nepoužívá:
Func<int, int, int> constant = (_, _) => 42;
Parametry ignorování lambda mohou být užitečné při použití lambda výrazu k poskytnutí obslužné rutiny události.
Poznámka
Pro zpětnou kompatibilitu, pokud je pojmenován _pouze jeden vstupní parametr , kompilátor považuje _ za název tohoto parametru v rámci výrazu lambda.
Počínaje jazykem C# 12 můžete zadat výchozí hodnoty pro seznamy explicitně zadaných parametrů. Syntaxe a omezení výchozích hodnot parametrů jsou stejné jako u metod a místních funkcí. Následující příklad deklaruje výraz lambda s výchozím parametrem a potom ho jednou zavolá pomocí výchozího a jednou se dvěma explicitními parametry:
var IncrementBy = (int source, int increment = 1) => source + increment;
Console.WriteLine(IncrementBy(5)); // 6
Console.WriteLine(IncrementBy(5, 2)); // 7
Výrazy lambda můžete deklarovat také pomocí params polí nebo kolekcí jako poslední parametr v seznamu explicitně zadaných parametrů:
var sum = (params IEnumerable<int> values) =>
{
int sum = 0;
foreach (var value in values)
sum += value;
return sum;
};
var empty = sum();
Console.WriteLine(empty); // 0
var sequence = new[] { 1, 2, 3, 4, 5 };
var total = sum(sequence);
Console.WriteLine(total); // 15
V rámci těchto aktualizací, když je skupině metod, která má výchozí parametr, přiřazena lambda výraz, tento lambda výraz má také stejný výchozí parametr. Skupinu metod s parametrem kolekce params lze také přiřadit výrazu lambda.
Výrazy lambda s výchozími parametry nebo kolekcemi params jako parametry nemají přirozené typy, které odpovídají Func<> nebo Action<> typům. Můžete ale definovat typy delegátů, které obsahují výchozí hodnoty parametrů:
delegate int IncrementByDelegate(int source, int increment = 1);
delegate int SumDelegate(params int[] values);
delegate int SumCollectionDelegate(params IEnumerable<int> values);
Nebo můžete k definování typu delegáta použít implicitně zadané proměnné s deklaracemi var. Kompilátor syntetizuje správný typ delegáta.
Další informace o výchozích parametrech pro výrazy lambda naleznete ve specifikaci funkce pro výchozí parametry výrazů lambda.
Asynchronní lambda
Pomocí klíčových slov async a await můžete snadno vytvořit výrazy a příkazy lambda, které zahrnují asynchronní zpracování. Například následující příklad Windows Forms obsahuje obslužnou rutinu události, která volá a očekává asynchronní metodu ExampleMethodAsync.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += button1_Click;
}
private async void button1_Click(object sender, EventArgs e)
{
await ExampleMethodAsync();
textBox1.Text += "\r\nControl returned to Click event handler.\n";
}
private async Task ExampleMethodAsync()
{
// The following line simulates a task-returning asynchronous process.
await Task.Delay(1000);
}
}
Stejnou obslužnou rutinu události můžete přidat pomocí asynchronního lambda. Chcete-li přidat tuto obslužnou rutinu, přidejte před seznam parametrů lambda modifikátor async, jak ukazuje následující příklad:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += async (sender, e) =>
{
await ExampleMethodAsync();
textBox1.Text += "\r\nControl returned to Click event handler.\n";
};
}
private async Task ExampleMethodAsync()
{
// The following line simulates a task-returning asynchronous process.
await Task.Delay(1000);
}
}
Další informace o tom, jak vytvářet a používat asynchronní metody, naleznete v tématu Asynchronní programování pomocí async a await.
Výrazy lambda a n-tice
Jazyk C# poskytuje integrovanou podporu pro n-tice. Jako argument funkce lambda můžete zadat n-tici a funkce lambda může také vrátit n-tici. V některých případech kompilátor jazyka C# používá odvození typu k určení typů elementů řazené kolekce členů.
Řazenou kolekci členů definujete uzavřením čárkami odděleného seznamu jejích součástí do závorek. Následující příklad používá řazenou kolekci členů se třemi komponentami k předání posloupnosti čísel výrazu lambda, který zdvojnásobí každou hodnotu a vrátí řazenou kolekci členů se třemi komponentami, které obsahují výsledek násobení.
Func<(int, int, int), (int, int, int)> doubleThem = ns => (2 * ns.Item1, 2 * ns.Item2, 2 * ns.Item3);
var numbers = (2, 3, 4);
var doubledNumbers = doubleThem(numbers);
Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");
// Output:
// The set (2, 3, 4) doubled: (4, 6, 8)
Obvykle se pole n-tice nazývají Item1, Item2atd. Můžete však definovat řazenou kolekci členů s pojmenovanými komponentami, jak to dělá následující příklad.
Func<(int n1, int n2, int n3), (int, int, int)> doubleThem = ns => (2 * ns.n1, 2 * ns.n2, 2 * ns.n3);
var numbers = (2, 3, 4);
var doubledNumbers = doubleThem(numbers);
Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");
Další informace o n-ticích v C# najdete v tématu Typy n-tic.
Lambdas se standardními operátory dotazů
LINQ to Objects, mimo jiné implementace, používá vstupní parametr, jehož typ je jedním z Func<TResult> rodin obecných delegátů. Tito delegáti používají parametry typu k definování počtu a typu vstupních parametrů a návratového typu delegáta.
Func delegáti jsou užiteční pro zapouzdření výrazů definovaných uživatelem, které se aplikují na každý prvek v sadě zdrojových dat. Představte si například typ delegáta Func<T,TResult>:
public delegate TResult Func<in T, out TResult>(T arg)
Vytvoří instanci delegáta jako Func<int, bool> instanci, kde int je vstupní parametr a bool je návratovou hodnotou. Vrácená hodnota je vždy zadána v posledním parametru typu. Například Func<int, string, bool> definuje delegáta se dvěma vstupními parametry, int a stringa návratovým typem bool. Následující Func delegát při vyvolání vrátí logickou hodnotu, která označuje, zda je vstupní parametr roven pěti:
Func<int, bool> equalsFive = x => x == 5;
bool result = equalsFive(4);
Console.WriteLine(result); // False
Výraz lambda můžete také zadat, pokud je typ argumentu Expression<TDelegate>, například ve standardních operátorech dotazu, které Queryable typ definuje. Když zadáte Expression<TDelegate> argument, lambda se zkompiluje do stromu výrazů.
Následující příklad používá Count standardní operátor dotazu:
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Console.WriteLine($"There are {oddNumbers} odd numbers in {string.Join(" ", numbers)}");
Kompilátor může odvodit typ vstupního parametru nebo ho můžete explicitně zadat. Tento konkrétní výraz lambda spočítá celá čísla (n), které při dělení dvěma mají zbytek 1.
Následující příklad vytvoří sekvenci, která obsahuje všechny prvky v poli numbers, která předchází 9, protože to je první číslo v posloupnosti, která nesplňuje podmínku:
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstNumbersLessThanSix = numbers.TakeWhile(n => n < 6);
Console.WriteLine(string.Join(" ", firstNumbersLessThanSix));
// Output:
// 5 4 1 3
Následující příklad určuje více vstupních parametrů uzavřením do závorek. Metoda vrátí všechny prvky v numbers matice, dokud nenajde číslo, jehož hodnota je menší než jeho pořadová pozice v matici:
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
Console.WriteLine(string.Join(" ", firstSmallNumbers));
// Output:
// 5 4
Výrazy lambda nepoužíváte přímo ve výrazech dotazu , ale můžete je použít ve volání metod ve výrazech dotazu, jak ukazuje následující příklad:
var numberSets = new List<int[]>
{
new[] { 1, 2, 3, 4, 5 },
new[] { 0, 0, 0 },
new[] { 9, 8 },
new[] { 1, 0, 1, 0, 1, 0, 1, 0 }
};
var setsWithManyPositives =
from numberSet in numberSets
where numberSet.Count(n => n > 0) > 3
select numberSet;
foreach (var numberSet in setsWithManyPositives)
{
Console.WriteLine(string.Join(" ", numberSet));
}
// Output:
// 1 2 3 4 5
// 1 0 1 0 1 0 1 0
Určování typu ve výrazech lambda
Při psaní lambda často nemusíte zadávat typ vstupních parametrů. Kompilátor odvodí typ na základě těla lambda, typů parametrů a dalších faktorů popsaných ve specifikaci jazyka C#. U většiny standardních operátorů dotazu je prvním vstupem typ prvků ve zdrojové sekvenci. Pokud dotazujete IEnumerable<Customer>objekt, vstupní proměnná se odvozuje jako Customer objekt, což znamená, že máte přístup k jeho metodám a vlastnostem:
customers.Where(c => c.City == "London");
Obecná pravidla pro odvození typu pro lambda jsou:
- Lambda musí obsahovat stejný počet parametrů jako typ delegáta.
- Každý vstupní parametr v lambda musí být implicitně konvertibilní na odpovídající parametr delegáta.
- Návratová hodnota lambda (pokud existuje) musí být implicitně konvertibilní na návratový typ delegáta.
Přirozený typ výrazu lambda
Výraz lambda nemá typ, protože běžný systém typů nemá žádný vnitřní koncept výrazu lambda. Někdy je ale vhodné mluvit neformálně o "typu" výrazu lambda. Tento neformální "typ" odkazuje na typ delegáta nebo Expression typ, na který je výraz lambda převeden.
Výraz lambda může mít přirozený typ. Místo toho, abyste museli deklarovat typ delegáta, jako Func<...> je výraz lambda nebo Action<...> výraz lambda, kompilátor odvodí typ delegáta z výrazu lambda. Představte si například následující deklaraci:
var parse = (string s) => int.Parse(s);
Kompilátor odvodíparse, že je to .Func<string, int> Kompilátor zvolí dostupného delegáta Func nebo Action, pokud je k dispozici vhodný. V opačném případě syntetizuje typ delegáta. Například typ delegáta je syntetizován, pokud výraz lambda má ref parametry. Pokud má výraz lambda přirozený typ, můžete ho přiřadit méně explicitnímu typu, například System.ObjectSystem.Delegate:
object parse = (string s) => int.Parse(s); // Func<string, int>
Delegate parse = (string s) => int.Parse(s); // Func<string, int>
Skupiny metod (to znamená názvy metod bez seznamů parametrů) s přesně jedním přetížením mají přirozený typ:
var read = Console.Read; // Just one overload; Func<int> inferred
var write = Console.Write; // ERROR: Multiple overloads, can't choose
Pokud přiřadíte výraz lambda k System.Linq.Expressions.LambdaExpressionnebo System.Linq.Expressions.Expressiona lambda má přirozený typ delegáta, výraz má přirozený typ System.Linq.Expressions.Expression<TDelegate>, s typem přirozeného delegáta použitým jako argument parametru typu:
LambdaExpression parseExpr = (string s) => int.Parse(s); // Expression<Func<string, int>>
Expression parseExpr = (string s) => int.Parse(s); // Expression<Func<string, int>>
Ne všechny výrazy lambda mají přirozený typ. Představte si následující deklaraci:
var parse = s => int.Parse(s); // ERROR: Not enough type info in the lambda
Kompilátor nemůže odvodit typ parametru pro s. Pokud kompilátor nemůže odvodit přirozený typ, musíte deklarovat typ:
Func<string, int> parse = s => int.Parse(s);
Explicitní návratový typ
Návratový typ výrazu lambda je obvykle zřejmý a odvozený. U některých výrazů nefunguje odvození:
var choose = (bool b) => b ? 1 : "two"; // ERROR: Can't infer return type
Před vstupními parametry můžete zadat návratový typ výrazu lambda. Pokud zadáte explicitní návratový typ, je nutné dát vstupní parametry do závorek.
var choose = object (bool b) => b ? 1 : "two"; // Func<bool, object>
Atributy
Do výrazu lambda a jeho parametrů můžete přidat atributy. Následující příklad ukazuje, jak přidat atributy do výrazu lambda:
Func<string?, int?> parse = [ProvidesNullCheck] (s) => (s is not null) ? int.Parse(s) : null;
Můžete také přidat atributy ke vstupním parametrům nebo návratové hodnotě, jak ukazuje následující příklad:
var concat = ([DisallowNull] string a, [DisallowNull] string b) => a + b;
var inc = [return: NotNullIfNotNull(nameof(s))] (int? s) => s.HasValue ? s++ : null;
Jak ukazují předchozí příklady, je nutné uzavřít vstupní parametry do závorek, když přidáváte atributy k lambda výrazu nebo jeho parametrům.
Důležitý
Výrazy lambda vyvoláte prostřednictvím základního typu delegáta. Toto vyvolání se liší od metod a místních funkcí. Metoda Invoke delegáta nekontroluje atributy výrazu lambda. Atributy nemají žádný vliv při vyvolání výrazu lambda. Atributy výrazů lambda jsou užitečné pro analýzu kódu a lze je zjistit prostřednictvím reflexe. Jedním z důsledků tohoto rozhodnutí je, že System.Diagnostics.ConditionalAttribute se nedá použít u výrazu lambda.
Zachycení vnějších proměnných a rozsahu proměnných ve výrazech lambda
Lambdy mohou odkazovat na vnější proměnné. Tyto vnější proměnné jsou proměnné, které jsou ve scope metody, která definuje výraz lambda, nebo ve scope typu, který výraz lambda obsahuje. Pokud tímto způsobem zachytíte proměnné, výraz lambda je uloží pro použití i v případě, že proměnné přejdou mimo rozsah a normálně by byly uvolněny z paměti. Před jeho používáním ve výrazu lambda musíte určitě přiřadit vnější proměnnou. Následující příklad ukazuje tato pravidla:
public static class VariableScopeWithLambdas
{
public class VariableCaptureGame
{
internal Action<int>? updateCapturedLocalVariable;
internal Func<int, bool>? isEqualToCapturedLocalVariable;
public void Run(int input)
{
int j = 0;
updateCapturedLocalVariable = x =>
{
j = x;
bool result = j > input;
Console.WriteLine($"{j} is greater than {input}: {result}");
};
isEqualToCapturedLocalVariable = x => x == j;
Console.WriteLine($"Local variable before lambda invocation: {j}");
updateCapturedLocalVariable(10);
Console.WriteLine($"Local variable after lambda invocation: {j}");
}
}
public static void Main()
{
var game = new VariableCaptureGame();
int gameInput = 5;
game.Run(gameInput);
int jTry = 10;
bool result = game.isEqualToCapturedLocalVariable!(jTry);
Console.WriteLine($"Captured local variable is equal to {jTry}: {result}");
int anotherJ = 3;
game.updateCapturedLocalVariable!(anotherJ);
bool equalToAnother = game.isEqualToCapturedLocalVariable(anotherJ);
Console.WriteLine($"Another lambda observes a new value of captured variable: {equalToAnother}");
}
// Output:
// Local variable before lambda invocation: 0
// 10 is greater than 5: True
// Local variable after lambda invocation: 10
// Captured local variable is equal to 10: True
// 3 is greater than 5: False
// Another lambda observes a new value of captured variable: True
}
Následující pravidla platí pro dosah proměnných ve výrazech lambda:
- Proměnná, kterou zachytíte, se neshromažďuje, dokud delegát, který na něj odkazuje, nebude způsobilý pro uvolňování paměti.
- Proměnné, které zavádíte ve výrazu lambda, nejsou viditelné v ohraničující metodě.
- Výraz lambda nemůže přímo zachytit v, refnebo out parametr z uzavírající metody.
- Příkaz return ve výrazu lambda nezpůsobí, že metoda, která jej obsahuje, vrátí hodnotu.
- Výraz lambda nemůže obsahovat příkazy goto, breaknebo continue, pokud cíl těchto skokových příkazů je mimo blok výrazu lambda. Je také chybou mít příkaz skoku mimo blok lambda výrazu, pokud je cíl uvnitř bloku.
Pokud chcete zabránit neúmyslnému zachycení místních proměnných nebo stavu instance lambda, použijte static modifikátor na výraz lambda:
Func<double, double> square = static x => x * x;
Statická lambda nemůže zachytit místní proměnné nebo stav instance z uzavřených oborů, ale může odkazovat na statické členy a definice konstant.
Specifikace jazyka C#
Další informace najdete v části Anonymní výrazy funkce specifikace jazyka jazyka C#.
Další informace o těchto funkcích najdete v následujících poznámkách k návrhu funkcí: