TOC

The community is working on translating this tutorial into Macedonian, but it seems that no one has started the translation process for this article yet. If you can help us, then please click "More info".

Classes:

Local functions

We learned in previous articles that methods and properties belong to classes in C#. Inside methods, you can have local variables, which are variables only accessible in the scope of this specific method. This makes sense because you'll often have temporary data you need to store, but which shouldn't be accessible from other classes or even other methods in the same class. Previously, you couldn't do the same thing with methods - if a method was declared on a class, it could, at least, be accessed from other methods within the same class, but in C# version 7, the concept of local functions were introduced.

A local function is declared inside an existing method and can only be accessed from this method. This encapsulates functionality very tightly and also makes it clear to any readers of your code that this functionality is only relevant for the declaring method. A local function looks like a regular method, but without the visibility modifier, since a local function is always only accessible from within the declaring method. Here's an example:

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));
}

A quite silly example, but it does demonstrate how you can declare a local function (in this case called doesNameStartWithUppercaseChar) inside a method (called MethodWithLocalFunction) and then call it one or several times from inside the method.

As you can see in my example, I declare the local function in the start of the method. You are free to change this, e.g. by declaring it in the middle or the end of the method. Only in one case will it make a difference: A local function is allowed to access variables declared inside the declaring method, but only if they have been declared before the local function. So if you want to take advantage of this behavior, you will have to modify the method, e.g. like this:

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]);

	}
}

Notice how I declare the nameMaxLength variable inside the method and then access it inside the local function.

Static local functions

In C# version 8, support for static local functions were added. As of writing, the only difference between a regular and a static local function is the fact that a static local function can't use variables from the declaring method - in other words, they no longer share scope. So if you want to make sure that your local function can't reference or change variables from the method, just declare it as static, like this:

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

In some situations, local functions can be a great little helper when you need to encapsulate and reuse very specific functionality. As an alternative, if the functionality can be reused from other methods, you may want to consider adding it as a real method on a helper class or as an extension method.


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!