Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Выберите локальную функцию, если вы хотите дать вспомогательной логике описательное имя и только одному элементу нужен этот вспомогательный метод. По сравнению с методом внешнего типа локальная функция сообщает читателям, что вспомогательная функция принадлежит этой операции. Это название позволяет читателям понять основную логику, прежде чем они изучают детали.
Tip
В этой статье описано практическое использование локальных функций для разработчиков, которые знают, как писать методы. Подробные сведения об объявлении и правилах диагностики см. в разделе Объявления локальных функций.
Локальная функция — это именованная функция, объявленная внутри другого элемента, например метода или конструктора. Его имя находится в области видимости только в пределах этого члена.
Следующая форма объявляет и вызывает локальную функцию:
string CreateMessage(string name)
{
return AddGreeting(name);
string AddGreeting(string value) => $"Hello, {value}!";
}
AddGreeting остается внутри CreateMessage. Другие члены типа не могут вызвать этот элемент.
Сохранение вспомогательной логики рядом с местом вызова
Локальные функции позволяют основному ходу выполнения описывать, что происходит, а вспомогательные функции объясняют отдельные шаги. В следующем примере создается план чтения. В нем используется локальная функция для добавления задания одной недели за раз.
IReadOnlyList<ReadingWeek> plan =
CreateReadingPlan("The Long Way Home", chapterCount: 10, chaptersPerWeek: 3);
foreach (ReadingWeek week in plan)
{
Console.WriteLine(
$"Week {week.Number}: {week.BookTitle}, chapters {week.FirstChapter}-{week.LastChapter}");
}
static IReadOnlyList<ReadingWeek> CreateReadingPlan(
string bookTitle,
int chapterCount,
int chaptersPerWeek)
{
var plan = new List<ReadingWeek>();
AddWeek(firstChapter: 1);
return plan;
void AddWeek(int firstChapter)
{
if (firstChapter > chapterCount)
{
return;
}
int lastChapter = Math.Min(
firstChapter + chaptersPerWeek - 1,
chapterCount);
plan.Add(new ReadingWeek(
plan.Count + 1,
bookTitle,
firstChapter,
lastChapter));
AddWeek(lastChapter + 1);
}
}
ReadingWeek записывает номер недели и диапазон глав, назначенных для этой недели.
AddWeek относится только к CreateReadingPlan, поэтому его локальная область видимости отражает эту связь.
Вы можете объявить локальную функцию до или после операторов, вызывающих ее. Поместите его туда, где читатели могут следить за основным ходом изложения, не теряя контекста.
Используйте параметры или сохраняйте локальное состояние
Нестатическая локальная функция может использовать параметры и локальные переменные содержащего её члена. Это использование называется захватом. В примере AddWeek плана чтения записывается следующее:
-
bookTitle,chapterCountиchaptersPerWeekизCreateReadingPlanпараметров. -
planиз локальной переменной.
Захват полезен, когда вспомогательная функция естественно принадлежит окружающей операции. Сохраняйте только несколько очевидных значений. Если помощник может получать все свои данные с помощью параметров, рассмотрите локальную static функцию:
static int LastChapter(int firstChapter, int chaptersPerWeek, int chapterCount) =>
Math.Min(firstChapter + chaptersPerWeek - 1, chapterCount);
Статическая локальная функция не может захватывать локальные переменные, параметры или состояние экземпляра из содержащего элемента. Его параметры делают предоставленные данные явными, а захват позволяет помощнику использовать контекст его содержащей операции.
Используйте рекурсию, когда проблема повторяется
Рекурсия возникает, когда функция вызывает саму себя с меньшей частью проблемы.
AddWeek В примере добавляет одну неделю, а затем вызывает себя с первой главой для следующей недели.
Для каждой рекурсивной функции требуется условие, которое останавливает вызовы.
AddWeek останавливается, когда firstChapter превышает chapterCount:
if (firstChapter > chapterCount)
{
return;
}
Локальные функции хорошо подходят для рекурсии, так как их имена находятся в области видимости по всему содержащему члену. Предпочитайте цикл, когда он более прямо выражает повторяющиеся действия. Выберите рекурсию, когда рекурсивная структура упрощает понимание алгоритма.
Выберите локальную функцию или лямбда-выражение
Локальные функции и лямбда-выражения могут использовать окружающие переменные, но они выражают разное назначение:
| Выберите | When |
|---|---|
| Локальная функция | Вспомогательный элемент выигрывает от наличия имени, имеет несколько инструкций, нуждается в рекурсии или вызывается из нескольких мест в содержающем элементе. |
| Лямбда-выражение | Вы передаете короткое поведение непосредственно другому методу или храните его в переменной делегата. |
В следующем коде используются обе формы:
static IEnumerable<string> DescribeShortWeeks(
IEnumerable<ReadingWeek> plan,
int maximumChapters)
{
return plan
.Where(IsShortWeek)
.Select(week => $"Week {week.Number}: {week.FirstChapter}-{week.LastChapter}");
bool IsShortWeek(ReadingWeek week) =>
week.LastChapter - week.FirstChapter + 1 <= maximumChapters;
}
Console.WriteLine("Short weeks:");
foreach (string description in DescribeShortWeeks(plan, maximumChapters: 2))
{
Console.WriteLine(description);
}
IsShortWeek присваивает правилу, переданному в Where, описательное имя. Лямбда, переданная в Select, выполняет одно короткое преобразование в точке использования. Обе формы передают поведение другому методу здесь. Локальная функция также может быть вспомогательной функцией, которую вы вызываете напрямую, как это показано для AddWeek в примере плана чтения.
Используйте обычный метод вместо локальной функции, если нескольким членам класса нужна эта вспомогательная функция. Используйте вместо этого лямбда-выражение, если поведение является коротким, а его назначение ясно из метода, принимающего лямбду.
Локальные функции также могут быть функциями-итераторами, которые содержат yield return. Для этого см. итераторы.