Локальные функции и лямбда-выражения
Выбирайте между локальными функциями и лямбда-выражениями.
«Локальные функции и лямбда-выражения» — бесплатный урок C# Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
Локальные функции и лямбда-выражения
И локальные функции, и лямбда-выражения позволяют определять небольшие функции внутри метода. Они во многом похожи, но различаются синтаксисом, возможностями и производительностью. Правильный выбор делает код понятным и эффективным.
Два способа написать вспомогательную функцию
Здесь одна и та же вспомогательная функция для возведения в квадрат записана и как локальная функция, и как лямбда-выражение, присвоенное делегату. Обе версии дают одинаковый результат.
using System;
public class Program
{
public static void Main()
{
int SquareFn(int n) => n * n; // local function
Func<int, int> squareLambda = n => n * n; // lambda
Console.WriteLine(SquareFn(4));
Console.WriteLine(squareLambda(4));
}
}Локальные функции упрощают рекурсию
Локальная функция может вызывать себя по имени. Лямбда-выражение не может ссылаться на собственную переменную во время инициализации, поэтому рекурсия с лямбда-выражениями неудобна.
using System;
public class Program
{
public static void Main()
{
// Clean recursion with a local function
int Factorial(int n) => n <= 1 ? 1 : n * Factorial(n - 1);
Console.WriteLine(Factorial(5));
}
}Локальные функции могут быть обобщёнными
Локальные функции поддерживают собственные параметры типов. Лямбда-выражения не могут быть обобщёнными, поэтому для повторно используемых типизированных вспомогательных функций следует выбирать локальную функцию.
using System;
public class Program
{
public static void Main()
{
T First<T>(T[] items) => items[0]; // generic local function
Console.WriteLine(First(new[] { 9, 8, 7 }));
Console.WriteLine(First(new[] { "a", "b" }));
}
}Производительность: избежание выделения делегата
Лямбда-выражение, присвоенное делегату, может выделить объект делегата, а при захвате — ещё и замыкание. Локальная функция, вызванная напрямую, избегает обоих выделений, что важно в горячих циклах.
using System;
public class Program
{
public static void Main()
{
// Direct call, no delegate or closure allocation
static int Add(int a, int b) => a + b;
int total = 0;
for (int i = 0; i < 5; i++) total = Add(total, i);
Console.WriteLine(total);
}
}Параметры out и ref
Локальные функции могут объявлять параметры out и ref, а лямбда-выражения через распространённые делегаты Func/Action — нет. Поэтому вспомогательные функции в стиле TryParse естественно реализовывать как локальные функции.
using System;
public class Program
{
public static void Main()
{
bool TryGetFirstDigit(string s, out int digit)
{
digit = 0;
foreach (var c in s)
if (char.IsDigit(c)) { digit = c - '0'; return true; }
return false;
}
if (TryGetFirstDigit("abc4", out int d))
Console.WriteLine(d);
}
}Лямбды особенно удобны как непосредственные аргументы
Если нужно передать поведение прямо в метод, например LINQ Where или Select, лямбда будет самым кратким и понятным вариантом.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var nums = new List<int> { 1, 2, 3, 4, 5, 6 };
var evens = nums.Where(n => n % 2 == 0).Select(n => n * 10);
Console.WriteLine(string.Join(", ", evens));
}
}Захват работает в обоих случаях
Обе формы могут захватывать переменные из внешнего контекста. Различие в основном заключается в синтаксисе и стоимости выделения памяти, а не в возможности захвата.
using System;
public class Program
{
public static void Main()
{
int bonus = 10;
int WithBonusFn(int n) => n + bonus; // captures
Func<int, int> withBonusLambda = n => n + bonus; // captures
Console.WriteLine(WithBonusFn(5));
Console.WriteLine(withBonusLambda(5));
}
}Определённое присваивание и порядок
Локальную функцию можно вызвать до её объявления в методе, а переменной-лямбде необходимо присвоить значение до использования. Благодаря этому локальные функции можно размещать более гибко.
using System;
public class Program
{
public static void Main()
{
Console.WriteLine(Triple(4)); // allowed before declaration
int Triple(int n) => n * 3;
}
}Как выбрать между ними
Используйте локальную функцию для именованных, повторно используемых, рекурсивных или обобщённых вспомогательных функций, а также для вспомогательных функций на часто выполняемом пути. Используйте лямбду для короткого поведения, передаваемого непосредственно в другой метод. Оба варианта — инструменты; выбирайте их исходя из цели.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
static bool IsPrime(int n)
{
if (n < 2) return false;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) return false;
return true;
}
var primes = Enumerable.Range(2, 20).Where(IsPrime);
Console.WriteLine(string.Join(", ", primes));
}
}Попробуйте сами
Используйте рекурсивную локальную функцию для основной логики, а лямбду — для встроенного преобразования LINQ, позволяя каждому средству выполнять наиболее подходящую для него задачу.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
// Local function: recursive, named, reusable
static int DigitCount(int n) => Math.Abs(n) < 10 ? 1 : 1 + DigitCount(n / 10);
var nums = new List<int> { 5, 42, 1000, 7, 88 };
// Lambda: concise inline projection
var report = nums.Select(n => n + " has " + DigitCount(n) + " digits");
foreach (var line in report) Console.WriteLine(line);
}
}Быстрая проверка
Сравните эти два конструкта.
Итоги
Локальные функции и лямбды во многом похожи, но имеют несколько важных различий.
- Локальные функции могут быть рекурсивными и обобщёнными, а также использовать
out/ref. - При прямом вызове они не требуют выделения памяти под делегат и замыкание.
- Лямбды наиболее кратки, когда используются как непосредственные аргументы методов, например LINQ.
- Выбирайте вариант исходя из цели: именованная повторно используемая вспомогательная функция или короткое встроенное поведение.
Часто задаваемые вопросы
Урок «Локальные функции и лямбда-выражения» бесплатный?
Да — полный текст урока «Локальные функции и лямбда-выражения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Локальные функции и лямбда-выражения»?
Выбирайте между локальными функциями и лямбда-выражениями. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Локальные функции и лямбда-выражения»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Объявление локальных функций
- Замыкания и захваченные переменные
- Статические локальные функции
- Локальные функции и лямбда-выражения