Local Functions In C#

Introduction

In this article, we are going to discuss Local Functions - an excellent new feature introduced in C# 7.0 and enhanced in C# 8.0 and C# 9.0. This article can be very useful for beginners, intermediates, and professionals.

We are going to cover,

  1. What is Local Function?
  2. Difference between Delegates and Local function?
  3. Local function with Generic.
  4. Local function with Ref.
  5. Local function with out.
  6. Local function with Params.
  7. Enhancement in C# 8.0 – Static Local functions
  8. Enhancement in C#9.0 – Attributes in local functions
  9. Benefits of local functions.

What is Local Function

Local functions are private methods that are defined in another function and private method scope is limited to another function /parent function only. This helps to simplify more complex code.

Let's try to understand the above definition using the below example,

Below code is written before C# 7.0, 

using System;

namespace Local_Function
{
    class Program
    {
        static void Main(string[] args)
        {

            Program p = new Program();
            Console.WriteLine("Result 1 :- {0}", p.Add(10, 20));
            Console.WriteLine("Result 2 :- {0}", p.Add(30, 20));
            Console.WriteLine("Result 3 :- {0}", p.Add(40, 20));
            
            Console.ReadKey();

        }
        private int Add(int a, int b)
        {
            return a + b;
        }
    }
}

In the above code, 

  1. Create Console Application
  2. Add a new private method called “Add” in the program class
  3. Create an instance of Program class and call add method 3 times with different values.
  4. Execute program which will produce below output.

Output

So far, it is simple and straightforward. Now we will write the same code using the wonderful feature Local function in the below code,

using System;

namespace Local_Function
{
    class Program
    {
        static void Main(string[] args)
        {
            int Add(int a, int b)
            {
                return a + b;
            }
            Console.WriteLine("Result 1 :- {0}", Add(10, 20));
            Console.WriteLine("Result 2 :- {0}", Add(30, 20));
            Console.WriteLine("Result 3 :- {0}", Add(40, 20));
            Console.ReadKey();
        }
    }
}

We have removed the private method “Add” from the program class and incorporate that “Add” method in the Main method itself. So now in the above code “Add” method becomes a local function. The scope of the “Add” method (local function) is, it can be accessed within the Main method only.

The output of the above code is,

In C# 7.0, you can use Async and unsafe modifier with a local function.

Difference between Delegates and Local Function?

Now the question in the mind is what is the benefit of the above code. We can achieve it using delegate types like Func<>, action<>, etc. Then why should we use a local function?

Delegates have below limitations,

  1. Delegates method cannot work with Generic but Local function can work with generic.
  2. The local function can work with ref and out variables but Delegates can not.
  3. It can not work with Param but local function can work with Param.

Local function with Generic

Let us write some code to understand local function with Generic,

using System;

namespace Local_Function
{
    class Program
    {
        static void Main(string[] args)
        {
            void Add<T>(T a, T b)
            {
                dynamic x = a;
                dynamic y = b;

                Console.WriteLine(x + y);
            }
            Add(10, 20);
            Add("Kirtesh", " Shah");
            Console.ReadKey();
        }
    }
}

In the above code,

  1. We have added a new generic local function “Add” which is taking two Type parameters a & b.
  2. Assign values to local dynamic variables x & y.
  3. Perform x+y and print on console.
  4. We have called Add method with two different parameters

               4.1 First we have pass integers – 10,20

               4.2 In the second call we have passed string – “Kirtesh”, “Shah”

In the first case, the addition will be performed and the output would be 30. In the second case, it will concatenate both the string and output would be “Kirtesh Shah”.

Output

Local function with ref variables

Below code explains local function with the ref,

using System;

namespace Local_Function
{
    class Program
    {
        static void Main(string[] args)
        {
            void Math(int a, int b, ref int c, ref int d)
            {
                c = a + b;
                d = a * b;
            }

            int add=0, mul = 0;
            Math(10, 20, ref add, ref mul);

            Console.WriteLine("Addition  :- {0}", add);
            Console.WriteLine("Multi  :- {0}", mul);
            Console.ReadKey();
        }
    }
}

In the above code,

  1. Create Location function called “Math”, which will take 2 integer values and 2 ref integer parameters.
  2. Perform Addition and multiplication and assign the output to ref integer variables.
  3. Call math local function.
  4. Print ref variables values on the console.

Output

Local function with Out variables

We will use the ref section example to explain Out variable.

using System;

namespace Local_Function
{
    class Program
    {
        static void Main(string[] args)
        {
            void Math(int a, int b, out int c, out int d)
            {
                c = a + b;
                d = a * b;
            }
            Math(10, 20, out int add, out int mul);
            Console.WriteLine("Addition  :- {0}", add);
            Console.WriteLine("Multi  :- {0}", mul);
            Console.ReadKey();
        }
    }
}

In the above code,

  1. Create Location function called “Math”, which will take 2 integer values and 2 out integer parameters.
  2. Perform Addition and multiplication and assign the output to the out integer variables.
  3. Call math local function.
  4. Print out variable values on the console.

Output

Local function with Params

Params are mainly used to pass a set of numbers. Let us see the below code,

using System;

namespace Local_Function
{
    class Program
    {
        static void Main(string[] args)
        {
            void AddNumbers(params int[] intArr)
            {
                int result = 0;
                foreach (var item in intArr)
                {
                    result += item;
                }
                Console.WriteLine("Sum of Given numbers is :- {0}", result);
            }

            AddNumbers(10, 20, 30);
            AddNumbers(5, 2, 3, 10, 50);
            Console.ReadKey();
        }
    }
}

In the above code,

  1. Created new local function “AddNumbers” which takes a set of integer numbers as params(parameters of the method). This function performs additions of all numbers and prints on the console. 
  2. Call “AddNumbers” local function with different parameters.

Enhancement in C# 8.0 – Static Local function

The static local function is introduced in C# 8.0. To make a static local function, we just need to add the “Static” keyword with a local function that is not possible in C# 7.0.

using System;

namespace Local_functions
{
    class Program
    {
        static void Main(string[] args)
        {
           static void Addnumbers(int num1,int num2)
            {
                Console.WriteLine("Sum of given numbers :- {0}", num1 + num2);
            }
            Addnumbers(10, 20);
            Addnumbers(5, 5);
            Console.ReadLine();
        }
    }
}

Output

In the above code,

  1. Created static local function “Addnumbers” with two integer parameters.
  2. In the method, adding two number print output on the console.
  3. Call the “Addnumbers” method with different values.

Static function can not access variable declared in parent method like normal local function in C# 7.0. Let's discuss this statement in the below code,

using System;

namespace Local_functions
{
    class Program
    {
        static void Main(string[] args)
        {
            int num1=0, num2 = 0;

           static void Addnumbers()
            {
                Console.WriteLine("Sum of given numbers :- {0}", num1 + num2);
            }
            Addnumbers();
            Console.ReadLine();
        }
    }
}

This code will not compile and gave the below compile-time error.

The local function can access static local function.

using System;

namespace Local_functions
{
    class Program
    {
        static void Main(string[] args)
        {
            int num1 = 10;
            int num2 = 20;

            void add()
            {
                Addnumbers(num1, num2);
            }
           static void Addnumbers(int num1,int num2)
            {
                Console.WriteLine("Sum of given numbers :- {0}", num1 + num2);
            }
            add();
            Console.ReadLine();
        }
    }
}

Output

In the above code,

  1. We have created the local function “add” which is called static local function.
  2. Created another static local function “Addnumbers”. This function will do the addition of 2 integer parameter values and print output on the console.
  3. Call the “add” method and see the output.

The static local function can only access another static local function.

Let's discuss the below code,

using System;

namespace Local_functions
{
    class Program
    {
        static void Main(string[] args)
        {
            static void add()
            {
                Addnumbers(10, 20);
            }
            void Addnumbers(int num1,int num2)
            {
                Console.WriteLine("Sum of given numbers :- {0}", num1 + num2);
            }
            add();
            Console.ReadLine();
        }
    }
}

This code throws a compile-time error. See the below screen,

Enhancement in C# 9.0 - Attributes in local functions

C# 9.0 allows attributes on local functions. To understand this we will start with a simple example.

Suppose you have a local function that can be executed only when you are debugging your code. Before C# 9.0, we will use the below code,

using System;
using System.Diagnostics.CodeAnalysis;

namespace Local_functions
{
    class Program
    {
        static void Main(string[] args)
        {
            static void TestDebug()
            {
                Console.WriteLine("DEBUG - Test function");

            }
#if DEBUG
            TestDebug();
#endif

            Console.ReadLine();
        }
    }
}

Output

As we have executed our code in debug mode, we will get the above output.

Now we will use conditional attributes to apply attributes to a local function. Same code using C# 9.0,

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Local_functions
{
    class Program
    {
        static void Main(string[] args)
        {
            [Conditional("DEBUG")]
            static void TestDebug()
            {
                Console.WriteLine("DEBUG - Test function");

            }
            TestDebug();
            Console.ReadLine();
        }
    }
}

Similarly, you can use other attributes, see the below example of [NotNullWhen] attributes.

using System;
using System.Diagnostics.CodeAnalysis;

namespace Local_functions
{
    class Program
    {
        static void Main(string[] args)
        {
            static void Process(string? [] lines, string mark)
            {
                foreach (var line in lines)
                {
                    if (IsValid(line))
                    {
                        Console.WriteLine("true");
                    }
                }

                bool IsValid([NotNullWhen(true)] string? line)
                {
                    return !string.IsNullOrEmpty(line) && line.Length >= mark.Length;
                }
            }

            string[] str = new string[]
            {
                "Test1",
                "",
                null,
                "Line3"
            };
            Process(str, "test");
            Console.ReadLine();
        }
    }
}

Output

Below are a few benefits of Local functions,

  1. The Local functions can be defined inside the method, property (both getter and setter), and constructors.
  2. All local functions can access parent method arguments and variables.
  3. All local functions can access another local method define in the same scope.
  4. C# Compiler transforms local functions to Private methods.
  5. All local functions are private hence cannot declare method access modifier.
  6. Code becomes more clean and readable using local functions and also reduces the scope of error.
  7. Multiple local functions are allowed.    

That’s all for this article. Hope you enjoyed and learned about local functions in C#.