Local functions - C# Programming Guide (2023)

  • Article
  • 10 minutes to read

Local functions are methods of a type that are nested in another member. They can only be called from their containing member. Local functions can be declared in and called from:

  • Methods, especially iterator methods and async methods
  • Constructors
  • Property accessors
  • Event accessors
  • Anonymous methods
  • Lambda expressions
  • Finalizers
  • Other local functions

However, local functions can't be declared inside an expression-bodied member.

Note

In some cases, you can use a lambda expression to implement functionality also supported by a local function. For a comparison, see Local functions vs. lambda expressions.

Local functions make the intent of your code clear. Anyone reading your code can see that the method is not callable except by the containing method. For team projects, they also make it impossible for another developer to mistakenly call the method directly from elsewhere in the class or struct.

Local function syntax

A local function is defined as a nested method inside a containing member. Its definition has the following syntax:

(Video) C# Language Highlights: Local Functions

<modifiers> <return-type> <method-name> <parameter-list>

You can use the following modifiers with a local function:

  • async
  • unsafe
  • static A static local function can't capture local variables or instance state.
  • extern An external local function must be static.

All local variables that are defined in the containing member, including its method parameters, are accessible in a non-static local function.

Unlike a method definition, a local function definition cannot include the member access modifier. Because all local functions are private, including an access modifier, such as the private keyword, generates compiler error CS0106, "The modifier 'private' is not valid for this item."

The following example defines a local function named AppendPathSeparator that is private to a method named GetText:

private static string GetText(string path, string filename){ var reader = File.OpenText($"{AppendPathSeparator(path)}{filename}"); var text = reader.ReadToEnd(); return text; string AppendPathSeparator(string filepath) { return filepath.EndsWith(@"\") ? filepath : filepath + @"\"; }}

Beginning with C# 9.0, you can apply attributes to a local function, its parameters and type parameters, as the following example shows:

#nullable enableprivate static void Process(string?[] lines, string mark){ foreach (var line in lines) { if (IsValid(line)) { // Processing logic... } } bool IsValid([NotNullWhen(true)] string? line) { return !string.IsNullOrEmpty(line) && line.Length >= mark.Length; }}

The preceding example uses a special attribute to assist the compiler in static analysis in a nullable context.

Local functions and exceptions

One of the useful features of local functions is that they can allow exceptions to surface immediately. For method iterators, exceptions are surfaced only when the returned sequence is enumerated, and not when the iterator is retrieved. For async methods, any exceptions thrown in an async method are observed when the returned task is awaited.

The following example defines an OddSequence method that enumerates odd numbers in a specified range. Because it passes a number greater than 100 to the OddSequence enumerator method, the method throws an ArgumentOutOfRangeException. As the output from the example shows, the exception surfaces only when you iterate the numbers, and not when you retrieve the enumerator.

public class IteratorWithoutLocalExample{ public static void Main() { IEnumerable<int> xs = OddSequence(50, 110); Console.WriteLine("Retrieved enumerator..."); foreach (var x in xs) // line 11 { Console.Write($"{x} "); } } public static IEnumerable<int> OddSequence(int start, int end) { if (start < 0 || start > 99) throw new ArgumentOutOfRangeException(nameof(start), "start must be between 0 and 99."); if (end > 100) throw new ArgumentOutOfRangeException(nameof(end), "end must be less than or equal to 100."); if (start >= end) throw new ArgumentException("start must be less than end."); for (int i = start; i <= end; i++) { if (i % 2 == 1) yield return i; } }}// The example displays the output like this://// Retrieved enumerator...// Unhandled exception. System.ArgumentOutOfRangeException: end must be less than or equal to 100. (Parameter 'end')// at IteratorWithoutLocalExample.OddSequence(Int32 start, Int32 end)+MoveNext() in IteratorWithoutLocal.cs:line 22// at IteratorWithoutLocalExample.Main() in IteratorWithoutLocal.cs:line 11

If you put iterator logic into a local function, argument validation exceptions are thrown when you retrieve the enumerator, as the following example shows:

(Video) Local Functions In C# You Should Know…| Learn N Njoy...

public class IteratorWithLocalExample{ public static void Main() { IEnumerable<int> xs = OddSequence(50, 110); // line 8 Console.WriteLine("Retrieved enumerator..."); foreach (var x in xs) { Console.Write($"{x} "); } } public static IEnumerable<int> OddSequence(int start, int end) { if (start < 0 || start > 99) throw new ArgumentOutOfRangeException(nameof(start), "start must be between 0 and 99."); if (end > 100) throw new ArgumentOutOfRangeException(nameof(end), "end must be less than or equal to 100."); if (start >= end) throw new ArgumentException("start must be less than end."); return GetOddSequenceEnumerator(); IEnumerable<int> GetOddSequenceEnumerator() { for (int i = start; i <= end; i++) { if (i % 2 == 1) yield return i; } } }}// The example displays the output like this://// Unhandled exception. System.ArgumentOutOfRangeException: end must be less than or equal to 100. (Parameter 'end')// at IteratorWithLocalExample.OddSequence(Int32 start, Int32 end) in IteratorWithLocal.cs:line 22// at IteratorWithLocalExample.Main() in IteratorWithLocal.cs:line 8

Local functions vs. lambda expressions

At first glance, local functions and lambda expressions are very similar. In many cases, the choice between using lambda expressions and local functions is a matter of style and personal preference. However, there are real differences in where you can use one or the other that you should be aware of.

Let's examine the differences between the local function and lambda expression implementations of the factorial algorithm. Here's the version using a local function:

public static int LocalFunctionFactorial(int n){ return nthFactorial(n); int nthFactorial(int number) => number < 2 ? 1 : number * nthFactorial(number - 1);}

This version uses lambda expressions:

public static int LambdaFactorial(int n){ Func<int, int> nthFactorial = default(Func<int, int>); nthFactorial = number => number < 2 ? 1 : number * nthFactorial(number - 1); return nthFactorial(n);}

Naming

Local functions are explicitly named like methods. Lambda expressions are anonymous methods and need to be assigned to variables of a delegate type, typically either Action or Func types. When you declare a local function, the process is like writing a normal method; you declare a return type and a function signature.

Function signatures and lambda expression types

Lambda expressions rely on the type of the Action/Func variable that they're assigned to determine the argument and return types. In local functions, since the syntax is much like writing a normal method, argument types and return type are already part of the function declaration.

Beginning with C# 10, some lambda expressions have a natural type, which enables the compiler to infer the return type and parameter types of the lambda expression.

Definite assignment

Lambda expressions are objects that are declared and assigned at run time. In order for a lambda expression to be used, it needs to be definitely assigned: the Action/Func variable that it will be assigned to must be declared and the lambda expression assigned to it. Notice that LambdaFactorial must declare and initialize the lambda expression nthFactorial before defining it. Not doing so results in a compile time error for referencing nthFactorial before assigning it.

Local functions are defined at compile time. As they're not assigned to variables, they can be referenced from any code location where it is in scope; in our first example LocalFunctionFactorial, we could declare our local function either above or below the return statement and not trigger any compiler errors.

These differences mean that recursive algorithms are easier to create using local functions. You can declare and define a local function that calls itself. Lambda expressions must be declared, and assigned a default value before they can be re-assigned to a body that references the same lambda expression.

(Video) Practical C# - Local Functions in C# 7

Implementation as a delegate

Lambda expressions are converted to delegates when they're declared. Local functions are more flexible in that they can be written like a traditional method or as a delegate. Local functions are only converted to delegates when used as a delegate.

If you declare a local function and only reference it by calling it like a method, it will not be converted to a delegate.

Variable capture

The rules of definite assignment also affect any variables that are captured by the local function or lambda expression. The compiler can perform static analysis that enables local functions to definitely assign captured variables in the enclosing scope. Consider this example:

int M(){ int y; LocalFunction(); return y; void LocalFunction() => y = 0;}

The compiler can determine that LocalFunction definitely assigns y when called. Because LocalFunction is called before the return statement, y is definitely assigned at the return statement.

Note that when a local function captures variables in the enclosing scope, the local function is implemented as a delegate type.

Heap allocations

Depending on their use, local functions can avoid heap allocations that are always necessary for lambda expressions. If a local function is never converted to a delegate, and none of the variables captured by the local function are captured by other lambdas or local functions that are converted to delegates, the compiler can avoid heap allocations.

Consider this async example:

public async Task<string> PerformLongRunningWorkLambda(string address, int index, string name){ if (string.IsNullOrWhiteSpace(address)) throw new ArgumentException(message: "An address is required", paramName: nameof(address)); if (index < 0) throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative"); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException(message: "You must supply a name", paramName: nameof(name)); Func<Task<string>> longRunningWorkImplementation = async () => { var interimResult = await FirstWork(address); var secondResult = await SecondStep(index, name); return $"The results are {interimResult} and {secondResult}. Enjoy."; }; return await longRunningWorkImplementation();}

The closure for this lambda expression contains the address, index and name variables. In the case of local functions, the object that implements the closure may be a struct type. That struct type would be passed by reference to the local function. This difference in implementation would save on an allocation.

The instantiation necessary for lambda expressions means extra memory allocations, which may be a performance factor in time-critical code paths. Local functions do not incur this overhead. In the example above, the local functions version has two fewer allocations than the lambda expression version.

(Video) How to write Local Functions in C# | (Within WPF)

If you know that your local function won't be converted to a delegate and none of the variables captured by it are captured by other lambdas or local functions that are converted to delegates, you can guarantee that your local function avoids being allocated on the heap by declaring it as a static local function.

Tip

Enable .NET code style rule IDE0062 to ensure that local functions are always marked static.

Note

The local function equivalent of this method also uses a class for the closure. Whether the closure for a local function is implemented as a class or a struct is an implementation detail. A local function may use a struct whereas a lambda will always use a class.

public async Task<string> PerformLongRunningWork(string address, int index, string name){ if (string.IsNullOrWhiteSpace(address)) throw new ArgumentException(message: "An address is required", paramName: nameof(address)); if (index < 0) throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative"); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException(message: "You must supply a name", paramName: nameof(name)); return await longRunningWorkImplementation(); async Task<string> longRunningWorkImplementation() { var interimResult = await FirstWork(address); var secondResult = await SecondStep(index, name); return $"The results are {interimResult} and {secondResult}. Enjoy."; }}

Usage of the yield keyword

One final advantage not demonstrated in this sample is that local functions can be implemented as iterators, using the yield return syntax to produce a sequence of values.

public IEnumerable<string> SequenceToLowercase(IEnumerable<string> input){ if (!input.Any()) { throw new ArgumentException("There are no items to convert to lowercase."); } return LowercaseIterator(); IEnumerable<string> LowercaseIterator() { foreach (var output in input.Select(item => item.ToLower())) { yield return output; } }}

The yield return statement is not allowed in lambda expressions, see compiler error CS1621.

(Video) Exploring C# 7 - Local Functions

While local functions may seem redundant to lambda expressions, they actually serve different purposes and have different uses. Local functions are more efficient for the case when you want to write a function that is called only from the context of another method.

See also

  • Use local function instead of lambda (style rule IDE0039)
  • Methods

FAQs

What are local functions in C#? ›

Local functions are methods of a type that are nested in another member. They can only be called from their containing member. Local functions can be declared in and called from: Methods, especially iterator methods and async methods.

Are local functions Good C#? ›

Local functions are more efficient than lambdas when it comes to capturing local variables, and they don't require the use of delegates (which necessitates an object instantiation). They also restrict the scope of a segment of code to just where it's needed.

What is the difference between lambda and local function in C#? ›

The obvious difference from lambdas is that local functions have names and can be used without any indirection. Local functions can be recursive. The main semantical difference from lambda expressions is that local functions are not expressions, they are declaration statements.

What is the difference between local function and function C#? ›

The basic difference between both is that Func always returns a value while Action doesn't return a value. Local function can be defined in the body of any method. Both Func and Action delegates must be defined before they are called, while Local function can be defined later in the method.

What is the difference between local function and function? ›

In a function file, the first function in the file is called the main function. This function is visible to functions in other files, or you can call it from the command line. Additional functions within the file are called local functions, and they can occur in any order after the main function.

What are local or nested functions in C#? ›

C# Local Functions are a great new feature in C# 7. Local functions are nested functions. They are methods declared in another method and run in the context of that method. They are methods that are used only by one other method and help to keep each method small and focused.

Is C# faster than F#? ›

Task Runtime Performance

Asynchronous code (Tasks) in C# runs faster than in F# because the compiler supports them natively and generates optimized code. The difference may be reduced, once F# supports tasks natively, too.

Is C# good for gaming? ›

C# is a simple, modern, object-oriented, and type-safe programming language, and it's the most popular programming language in game development.

Is C# more performant than Python? ›

Additionally, due to its much simpler syntax, Python requires far fewer lines of code than C# does to execute the same task, theoretically making the process faster. And yet, C# proves to be quite the competition. In practice, C# programs actually run faster than Python ones, and they use up less memory to do it.

Why use local function instead of lambda? ›

Local functions are really just functions, no delegates are necessary. Also, local functions are more efficient with capturing local variables: lambdas usually capture variables into a class, while local functions can use a struct (passed using ref ), which again avoids an allocation.

Is lambda faster than normal function? ›

Benefits of using a Lambda function

Yes! you read it right. It doesn't cost you performance & as fast as normal function.

Why do we need lambda expressions in C#? ›

Lambda expressions are like anonymous methods but with much more flexibility. When using a lambda expression, you don't need to specify the type of the input. Hence, a lambda expression provides a shorter and cleaner way of representing anonymous methods.

Are local functions faster? ›

The only difference between local and global functions are it's scope. Technically local functions are faster than global ones (Sometimes ~10% faster).

How many types of functions are there in C#? ›

Basically, there are two types of Functions in C#. They are as follows: Built-in Functions. User-Defined Functions.

Can you create a function inside a function C#? ›

With C# 7 around, you can now declare such functions inside another the body of another function. Such functions are known as local functions. In other words, the support for local functions enables you to define a function within the scope of another function.

What are the 4 types of functions? ›

There are 4 types of functions:
  • Functions with arguments and return values. This function has arguments and returns a value: ...
  • Functions with arguments and without return values. ...
  • Functions without arguments and with return values. ...
  • Functions without arguments and without return values.

What are the three 3 different types of functions? ›

Types of Functions

Many – one function. Onto – function (Surjective Function) Into – function. Polynomial function.

What are the 7 types of functions? ›

Ans. 2 The different types of functions are many to one function, one to one function, onto function, one and onto function, constant function, the identity function, quadratic function, polynomial function, modulus function, rational function, signum function, greatest integer function.

What are the 4 types of functions in C? ›

What are the different categories of functions in C Programming?
  • Functions without arguments and without return values.
  • Functions without arguments and with return values.
  • Functions with arguments and without return values.
  • Functions with arguments and with return values.
Mar 9, 2021

Is it good practice to use nested functions? ›

When coding we want to hide immaterial detail. E.g. we mark functions as private as much as we can (well in python we can't really, so we use a leading underscore convention _like_this ). So that's a good reason to use nested functions — help the reader understand that the logic of bar will not be used anywhere else.

Is it good to have nested functions? ›

A nested function can access other local functions, variables, constants, types, classes, etc. that are in the same scope, or in any enclosing scope, without explicit parameter passing, which greatly simplifies passing data into and out of the nested function. This is typically allowed for both reading and writing.

Is C# harder to learn than Python? ›

In short, C# and Python are both high-level, object-oriented, and easy-to-learn languages. They ensure fast development and good performance. However, C# is more clear and organized, and it's much faster at runtime. While Python is easier to learn and write than C# and has vast standard libraries.

Is C sharp outdated? ›

C# is a programming language that was released in 2002 and is implemented in different of applications, including web development, desktop applications, and all phases of scripting languages. So it's not extremely old; compare it to PHP, Java, JavaScript and Python, which are all considerably older languages.

Does C# have a future? ›

C# is in very active development. The latest stable release is C# 10 that was released in November 2021 and introduced many improvements to the language. New major releases of C# are expected to be released every year alongside updates to the new unified . NET.

Which is faster Java or C#? ›

Calling the Java server 2000 times takes 2687 milliseconds. Calling the C# server 2000 times takes 214 milliseconds. The C# one is still much faster.

Is C# harder than C#? ›

C# is one of the easiest programming languages to learn. C# is a high-level, general-purpose programming language that is easy to read because of its well-defined class hierarchy. It is the perfect language for beginner developers as it will be straightforward to grasp compared to most other languages.

Is it difficult to learn C#? ›

C# is easy to learn

In addition to the time you can save during project development, you'll also spend less time learning C# as opposed to the more difficult programming languages out there. Thanks to its simplicity and easy-to-use features, C# offers a fairly low learning curve for beginners.

How long does it take to master C#? ›

It will take you about two to three months to learn the basics of C#, assuming you devote an hour or so a day to learning. You may learn C# quicker if you study part-time or full-time.

What is fastest programming language? ›

Compiled Languages

As a result, they tend to be faster and more efficient to execute than interpreted languages. They also give the developer more control over hardware aspects, like memory management and CPU usage. Examples of purely compiled languages are C, C++, Erlang, Haskell, Rust, and Go.

Should I learn C# or Python first? ›

Still, while Python is generally considered to be easier to learn, C# can give you a leg up as a first language since it's transferable to other languages in the C family, like C, C++, and Java. While Python is known for having concise, legible code, C# runs faster and is also clearer and more organized.

What are local choice functions? ›

Any function under a local Act other than a function specified or referred to in Regulation 2 or Schedule 1 (which may not be the responsibility of the Executive).

What are local and global functions in C? ›

A local variable is a variable that is declared inside a function. A global variable is a variable that is declared outside all functions. A local variable can only be used in the function where it is declared. A global variable can be used in all functions.

What are the 4 functions of local government? ›

Local government is responsible for a range of vital services for people and businesses in defined areas. Among them are well known functions such as social care, schools, housing and planning and waste collection, but also lesser known ones such as licensing, business support, registrar services and pest control.

What is local class with example? ›

Local classes are similar to inner classes because they cannot define or declare any static members. Local classes in static methods, such as the class PhoneNumber , which is defined in the static method validatePhoneNumber , can only refer to static members of the enclosing class.

How do local authorities make decisions? ›

The Council is made up of locally elected Councillors who make decisions on how to provide local services on behalf of local people. Decisions are made by democratic bodies, including the Full Council, The Cabinet, and other committees that Councillors are appointed to.

What is a local authority key decision? ›

Key decisions are significant decisions, made by the Full Council, a Committee or Sub-Committee of Council or an officer.

Videos

1. What are Local variables, functions & types in C# 9.0?
(Dotnetos)
2. C# Tutorial - Full Course for Beginners
(freeCodeCamp.org)
3. 13. C# - Functions/Methods
(Ervis Trupja)
4. Functions (and Timecode Apps) - Visual C# Programming Guide 2019 - Episode 5
(LucasSaturn - Animation Tutorials)
5. C# - Tutorial for Beginners in 15 MINUTES! - [ 2022 COMPLETE ]
(Skills Factory)
6. BUILD and HOST an ASP.NET Azure Functions Application EASILY
(tutorialsEU)
Top Articles
Latest Posts
Article information

Author: Msgr. Refugio Daniel

Last Updated: 05/14/2023

Views: 5679

Rating: 4.3 / 5 (54 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Msgr. Refugio Daniel

Birthday: 1999-09-15

Address: 8416 Beatty Center, Derekfort, VA 72092-0500

Phone: +6838967160603

Job: Mining Executive

Hobby: Woodworking, Knitting, Fishing, Coffee roasting, Kayaking, Horseback riding, Kite flying

Introduction: My name is Msgr. Refugio Daniel, I am a fine, precious, encouraging, calm, glamorous, vivacious, friendly person who loves writing and wants to share my knowledge and understanding with you.