What’s New in C# 10: Easier Lambda Expressions

This is part of a series on the new features introduced with C# 10.

Prior to C# 10, working with lambda expressions required a bit more code to be written, for example to explicitly define the delegate type such as Action<T> or Func<T>:

Action<string, ConsoleColor> writeWithColor = (string s, ConsoleColor color) =>
{
    var originalColor = Console.ForegroundColor;
    Console.ForegroundColor = color;
    Console.WriteLine(s);
    Console.ForegroundColor = originalColor;
};

Func<string, string> upper = (string s) => s.ToUpperInvariant();

writeWithColor("Hello", ConsoleColor.Cyan);
Console.WriteLine(upper("This should be default color"));
writeWithColor("Bye", ConsoleColor.Yellow);

Console.ReadLine();

Notice in the preceding code the lambda statement writeWithColor and the lambda expression upper both need explicit delegate types: Action<string, ConsoleColor> and Func<string, string>

From C# 10 we can make use of the new feature of “natural” lambda expression types.

This “natural type” is inferred by the compiler when it can, this means in C# we could just use var: var writeWithColor = (string s, ConsoleColor color) => etc. and var upper = (string s) => s.ToUpperInvariant();

This natural type inference will not always be possible, for example when you haven’t defined lambda parameter types like: var upper = (s) => s.ToUpperInvariant(); If you tried to compile this line of code you would get: Error    CS8917    The delegate type could not be inferred.

From C# 10, you can specify an explicit return type for a lambda expression where the compiler can’t work it out for you. You add the return type before the lambda parenthesis:

//Error CS8917 The delegate type could not be inferred
var createException = (bool b) => b ? new ArgumentNullException() : new DivideByZeroException();

// No error
var createException = Exception (bool b) => b ? new ArgumentNullException() : new DivideByZeroException();

You can also sometimes benefit from natural types for method groups:

// C#9
Func getUserInput = Console.ReadLine;
Action tellUser = (string s) => Console.WriteLine(s);
Func waitForEnter = Console.ReadLine;

tellUser("Please enter name");
var name = getUserInput();
tellUser($"Your name is {name}");
waitForEnter();

From C# 10 we could just use var:

// C#10
var getUserInput = Console.ReadLine;
var tellUser = (string s) => Console.WriteLine(s);
var waitForEnter = Console.ReadLine;

tellUser("Please enter name");
var name = getUserInput();
tellUser($"Your name is {name}");
waitForEnter();

You can’t however write: var write = Console.Write; because the Write method has multiple overloads so the compiler doesn’t know which one to choose.

SHARE:

Comments (6) -

  • FairDune

    11/26/2021 10:27:37 PM | Reply

    I think there are some syntax (formatting?) issues in the first snippet.
    The definition presumably should be : Action <string, ConsoleColor > writeWithColor

    Unless I am missing some C# 9 language feature.

    • Jason

      11/30/2021 2:42:02 AM | Reply

      Thanks - was a formatting error in the markup - should be fixed now Smile

  • shcv

    11/27/2021 5:37:10 AM | Reply

    I think your examples have extraneous </string> tags in them, because some tool is misinterpreting your parameterized types as XML tags...

    • Jason

      11/30/2021 2:42:40 AM | Reply

      Thanks - it was a formatting error fixed now Smile

  • Fons Sonnemans

    11/29/2021 2:24:42 PM | Reply

    Your first C# 9.0 codeblock doesn't compile.

    This is not valid C# code:
    Func<string string="" ,=""> upper = (string s) => s.ToUpperInvariant();

    Your scripts also end with </string></string>. Why?

    • Jason

      11/30/2021 2:43:43 AM | Reply

      Thanks Fons - it was a formatting error in sourecode markup during the blog writing - should be fixed now Smile

Add comment

Loading