TOC

This article is currently in the process of being translated into Russian (~98% done).

Классы:

Local functions

Из предыдущих статей мы узнали, что методы и свойства принадлежат классам в C#. Внутри методов у вас могут быть локальные переменные, которые являются переменными, доступными только в области действия этого конкретного метода. Это имеет смысл, потому что у вас часто будут временные данные, которые вам нужно сохранить, но которые не должны быть доступны из других классов или даже других методов в том же классе. Ранее вы не могли делать то же самое с методами - если метод был объявлен в классе, к нему можно было, по крайней мере, получить доступ из других методов внутри того же класса, но в C# версии 7 была введена концепция локальных функций.

Локальная функция объявлена внутри существующего метода, и доступ к ней возможен только из этого метода. Это очень плотно инкапсулирует функциональность, а также дает понять любому читателю вашего кода, что эта функциональность актуальна только для метода объявления. Локальная функция выглядит как обычный метод, но без модификатора видимости, поскольку локальная функция всегда доступна только из объявляющего метода. Вот пример:

public void MethodWithLocalFunction()
{
	bool doesNameStartWithUppercaseChar(string name)
	{
		if(String.IsNullOrEmpty(name))
			throw new Exception("name parameter must contain a value!");
		return Char.IsUpper(name[0]);
	}

	List<string> names = new List<string>()
	{
		"john doe",
		"Jane doe",
		"dog Doe"
	};

	foreach(string name in names)
		Console.WriteLine(name + " starts with uppercase char: " + doesNameStartWithUppercaseChar(name));
}

Довольно глупый пример, но он демонстрирует, как вы можете объявить локальную функцию (в данном случае называемую doesNameStartWithUppercaseChar) внутри метода (называемого MethodWithLocalFunction), а затем вызвать ее один или несколько раз изнутри метода.

Как вы можете видеть в моем примере, я объявляю локальную функцию в начале метода. Вы можете изменить это, например, объявив его в середине или в конце метода. Только в одном случае это будет иметь значение: локальной функции разрешен доступ к переменным, объявленным внутри метода объявления, но только в том случае, если они были объявлены до локальной функции. Поэтому, если вы хотите воспользоваться преимуществами этого поведения, вам придется изменить метод, например, следующим образом:

public void MethodWithLocalFunction()
{
	int nameMaxLength = 10;

	List<string> names = new List<string>()
	{
		"john doe",
		"Jane doe",
		"dog Doe"
	};

	foreach(string name in names)
		Console.WriteLine(name + " starts with uppercase char: " + doesNameStartWithUppercaseChar(name));

	bool doesNameStartWithUppercaseChar(string name)
	{
		if(String.IsNullOrEmpty(name))
			throw new Exception("name parameter must contain a value!");
		if(name.Length > nameMaxLength)
			throw new Exception("name is too long! Max length: " + nameMaxLength);
		return Char.IsUpper(name[0]);

	}
}

Обратите внимание, как я объявляю переменную nameMaxLength внутри метода, а затем получаю к ней доступ внутри локальной функции.

Static local functions

В C# версии 8 была добавлена поддержка статических локальных функций. На момент написания статьи единственной разницей между обычной и статической локальной функцией является тот факт, что статическая локальная функция не может использовать переменные из метода объявления - другими словами, они больше не имеют общей области видимости. Поэтому, если вы хотите убедиться, что ваша локальная функция не может ссылаться на переменные из метода или изменять их, просто объявите ее как статическую, например:

public void MethodWithLocalStaticFunction()
{
	int nameMaxLength = 10;
	
	static bool doesNameStartWithUppercaseChar(string name)
	{
		// Local variables, e.g. nameMaxLength, are no longer accessible here....
		if(String.IsNullOrEmpty(name))
			throw new Exception("name parameter must contain a value!");
		return Char.IsUpper(name[0]);
	}
	....
}

Summary

В некоторых ситуациях локальные функции могут быть отличным маленьким помощником, когда вам нужно инкапсулировать и повторно использовать очень специфическую функциональность. В качестве альтернативы, если функциональность может быть повторно использована из других методов, вы можете рассмотреть возможность добавления ее в качестве реального метода во вспомогательный класс или в качестве метода расширения.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!