How to make inline functions in C#

Yes, C# supports that. There are several syntaxes available.

  • Anonymous methods were added in C# 2.0:

    Func<int, int, int> add = delegate(int x, int y)
    {
        return x + y;
    };
    Action<int> print = delegate(int x)
    {
        Console.WriteLine(x);
    }
    Action<int> helloWorld = delegate // parameters can be elided if ignored
    {
        Console.WriteLine("Hello world!");
    }
    
  • Lambdas are new in C# 3.0 and come in two flavours.

    • Expression lambdas:

      Func<int, int, int> add = (int x, int y) => x + y; // or...
      Func<int, int, int> add = (x, y) => x + y; // types are inferred by the compiler
      
    • Statement lambdas:

      Action<int> print = (int x) => { Console.WriteLine(x); };
      Action<int> print = x => { Console.WriteLine(x); }; // inferred types
      Func<int, int, int> add = (x, y) => { return x + y; };
      
  • Local functions have been introduced with C# 7.0:

    int add(int x, int y) => x + y;
    void print(int x) { Console.WriteLine(x); }
    

There are basically two different types for these: Func and Action. Funcs return values but Actions don't. The last type parameter of a Func is the return type; all the others are the parameter types.

There are similar types with different names, but the syntax for declaring them inline is the same. An example of this is Comparison<T>, which is roughly equivalent to Func<T, T, int>.

Func<string, string, int> compare1 = (l,r) => 1;
Comparison<string> compare2 = (l, r) => 1;
Comparison<string> compare3 = compare1; // this one only works from C# 4.0 onwards

These can be invoked directly as if they were regular methods:

int x = add(23, 17); // x == 40
print(x); // outputs 40
helloWorld(x); // helloWorld has one int parameter declared: Action<int>
               // even though it does not make any use of it.

C# 7 adds support for local functions

Here is the previous example using a local function

void Method()
{
    string localFunction(string source)
    {
        // add your functionality here
        return source ;
    };

   // call the inline function
   localFunction("prefix");
}

The answer to your question is yes and no, depending on what you mean by "inline function". If you're using the term like it's used in C++ development then the answer is no, you can't do that - even a lambda expression is a function call. While it's true that you can define inline lambda expressions to replace function declarations in C#, the compiler still ends up creating an anonymous function.

Here's some really simple code I used to test this (VS2015):

    static void Main(string[] args)
    {
        Func<int, int> incr = a => a + 1;
        Console.WriteLine($"P1 = {incr(5)}");
    }

What does the compiler generate? I used a nifty tool called ILSpy that shows the actual IL assembly generated. Have a look (I've omitted a lot of class setup stuff)

This is the Main function:

        IL_001f: stloc.0
        IL_0020: ldstr "P1 = {0}"
        IL_0025: ldloc.0
        IL_0026: ldc.i4.5
        IL_0027: callvirt instance !1 class [mscorlib]System.Func`2<int32, int32>::Invoke(!0)
        IL_002c: box [mscorlib]System.Int32
        IL_0031: call string [mscorlib]System.String::Format(string, object)
        IL_0036: call void [mscorlib]System.Console::WriteLine(string)
        IL_003b: ret

See those lines IL_0026 and IL_0027? Those two instructions load the number 5 and call a function. Then IL_0031 and IL_0036 format and print the result.

And here's the function called:

        .method assembly hidebysig 
            instance int32 '<Main>b__0_0' (
                int32 a
            ) cil managed 
        {
            // Method begins at RVA 0x20ac
            // Code size 4 (0x4)
            .maxstack 8

            IL_0000: ldarg.1
            IL_0001: ldc.i4.1
            IL_0002: add
            IL_0003: ret
        } // end of method '<>c'::'<Main>b__0_0'

It's a really short function, but it is a function.

Is this worth any effort to optimize? Nah. Maybe if you're calling it thousands of times a second, but if performance is that important then you should consider calling native code written in C/C++ to do the work.

In my experience readability and maintainability are almost always more important than optimizing for a few microseconds gain in speed. Use functions to make your code readable and to control variable scoping and don't worry about performance.

"Premature optimization is the root of all evil (or at least most of it) in programming." -- Donald Knuth

"A program that doesn't run correctly doesn't need to run fast" -- Me

Tags:

C#

.Net