{"id":52245,"date":"2024-06-17T10:05:00","date_gmt":"2024-06-17T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=52245"},"modified":"2024-06-17T10:05:00","modified_gmt":"2024-06-17T17:05:00","slug":"refactor-your-code-with-default-lambda-parameters","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/refactor-your-code-with-default-lambda-parameters\/","title":{"rendered":"Refactor your code with default lambda parameters"},"content":{"rendered":"<p>This is the last post in a series of four, exploring various C# 12 features. In this post, we&#8217;ll explore the &#8220;default lambda parameters&#8221; feature, enabling developers to express default parameter values in lambdas. This series has covered a lot of ground:<\/p>\n<ol>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/csharp-primary-constructors-refactoring\/\">Refactor your C# code with primary constructors<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/refactor-your-code-with-collection-expressions\/\">Refactor your C# code with collection expressions<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/refactor-your-code-using-alias-any-type\/\">Refactor your C# code by aliasing any type<\/a><\/li>\n<li>Refactor your C# code to use default lambda parameters (this post)<\/li>\n<\/ol>\n<p>These features are part of our ongoing effort to enhance code readability and maintainability. Let&#8217;s explore them in detail!<\/p>\n<h2>Default Lambda Parameters \ud83e\uddee<\/h2>\n<p>Default lambda parameters are a new feature in C# 12 that allows developers to express default parameter values in lambdas. This feature is a natural extension of the existing default parameter feature in C# methods.<\/p>\n<h3>Before C# 12 \ud83d\udd70\ufe0f<\/h3>\n<p>Before C# 12, when you&#8217;d define a lambda expression that needed to provide some sort of default behavior you had to resort to using the null-coalescing operator (<code>??<\/code>) or the conditional operator (<code>?:<\/code>). Consider the following example:<\/p>\n<pre><code class=\"language-csharp\">var IncrementBy = static (int source, int? increment) =&gt;\n{\n    \/\/ Same as source + (increment.HasValue ? increment.Value : 1)\n    return source + (increment ?? 1);\n};\n\nConsole.WriteLine(IncrementBy(5, null)); \/\/ 6\nConsole.WriteLine(IncrementBy(5, 2));    \/\/ 7<\/code><\/pre>\n<h3>With C# 12 \ud83e\udd13<\/h3>\n<p>Instead, with default lambda parameters, you can now define default values for lambda parameters directly in the lambda expression. The syntax for default lambda parameters is similar to the syntax for default parameters in methods. The default value is specified after the parameter name and an equals sign (<code>=<\/code>). Consider the following example:<\/p>\n<pre><code class=\"language-csharp\">var IncrementBy = static (int source, int increment = 1) =&gt;\n{    \n    return source + increment;\n};\n\nConsole.WriteLine(IncrementBy(10));     \/\/ 11\nConsole.WriteLine(IncrementBy(10, 20)); \/\/ 30<\/code><\/pre>\n<p>Lambda expressions follow the same rules as methods when it comes to default parameters. The default value must be a compile-time constant, and it must be of the same type as the parameter. The default value is evaluated at <em>compile time<\/em>, and the parameter is optional when calling the lambda expression.<\/p>\n<pre><code class=\"language-csharp\">delegate int (int arg1, int arg2 = 1);<\/code><\/pre>\n<p>This means that when you could technically call the lambda expression with the name of the parameter but it has to be the name generated by the anonymous function. For example, consider the following extended example:<\/p>\n<pre><code class=\"language-csharp\">var IncrementByWithOffset = static (int source, int increment = 1, int offset = 100) =&gt;\n{    \n    return source + increment + offset;\n};\n\nConsole.WriteLine(IncrementByWithOffset(10));             \/\/ 111\nConsole.WriteLine(IncrementByWithOffset(10, 20));         \/\/ 130\nConsole.WriteLine(IncrementByWithOffset(10, 20, 0));      \/\/ 30\nConsole.WriteLine(IncrementByWithOffset(10, arg2: -100)); \/\/ 10\nConsole.WriteLine(IncrementByWithOffset(10, arg3: 0));    \/\/ 11<\/code><\/pre>\n<h3>ASP.NET Core Minimal API Example \ud83c\udf10<\/h3>\n<p>Let&#8217;s consider an example where we have an ASP.NET Core Minimal API that uses default lambda parameters. Using the <strong>File<\/strong> &gt; <strong>New<\/strong> &gt; <strong>Project<\/strong> dialog in Visual Studio 2022, create a new <strong>ASP.NET Core Web API<\/strong> project. Alternatively, you can use the following .NET CLI command to create a new project:<\/p>\n<pre><code class=\"language-bash\">dotnet new webapi -n WebApi<\/code><\/pre>\n<p>This template creates a new ASP.NET Core Web API project with a single <code>\/weatherforecast<\/code> endpoint. The <code>\/weatherforecast<\/code> endpoint returns an array of five random weather forecasts, consider the following template code from the <em>Program.cs<\/em> file:<\/p>\n<pre><code class=\"language-csharp\">var builder = WebApplication.CreateBuilder(args);\n\n\/\/ Add services to the container.\n\/\/ Learn more about configuring Swagger\/OpenAPI at https:\/\/aka.ms\/aspnetcore\/swashbuckle\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nvar app = builder.Build();\n\n\/\/ Configure the HTTP request pipeline.\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.UseHttpsRedirection();\n\nvar summaries = new[]\n{\n    \"Freezing\", \"Bracing\", \"Chilly\", \"Cool\", \"Mild\", \"Warm\", \"Balmy\", \"Hot\", \"Sweltering\", \"Scorching\"\n};\n\napp.MapGet(\"\/weatherforecast\", () =&gt;\n{\n    var forecast = Enumerable.Range(1, 5).Select(index =&gt;\n        new WeatherForecast\n        (\n            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),\n            Random.Shared.Next(-20, 55),\n            summaries[Random.Shared.Next(summaries.Length)]\n        ))\n        .ToArray();\n    return forecast;\n})\n.WithName(\"GetWeatherForecast\")\n.WithOpenApi();\n\napp.Run();\n\ninternal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)\n{\n    public int TemperatureF =&gt; 32 + (int)(TemperatureC \/ 0.5556);\n}<\/code><\/pre>\n<p>There&#8217;s a bit of code here from the template, and it&#8217;s not really the focus of our concern. Let&#8217;s focus on only the <code>MapGet<\/code> functionality, as it maps our lambda functionality to an HTTP GET call.<\/p>\n<pre><code class=\"language-csharp\">app.MapGet(\"\/weatherforecast\", () =&gt;\n{\n    var forecast = Enumerable.Range(1, 5).Select(index =&gt;\n        new WeatherForecast\n        (\n            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),\n            Random.Shared.Next(-20, 55),\n            summaries[Random.Shared.Next(summaries.Length)]\n        ))\n        .ToArray();\n    return forecast;\n})\n.WithName(\"GetWeatherForecast\")\n.WithOpenApi();<\/code><\/pre>\n<p>The <code>\/weatherforecast<\/code> endpoint returns an array of five weather forecasts. The hardcoded five in the <code>Enumerable.Range(1, 5)<\/code> method call can be replaced with a default lambda parameter, consider the following updated code snippet:<\/p>\n<pre><code class=\"language-csharp\">app.MapGet(\"\/weatherforecast\", (int days = 5) =&gt;\n{\n    \/\/ Safety check to ensure the days parameter is \n    \/\/ at least 1, but no more than 50.\n    var count = days is &gt; 0 and &lt;= 50 \n        ? days\n        : 5;\n\n    var forecast = Enumerable.Range(1, count).Select(index =&gt;\n        new WeatherForecast\n        (\n            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),\n            Random.Shared.Next(-20, 55),\n            summaries[Random.Shared.Next(summaries.Length)]\n        ))\n        .ToArray();\n\n    return forecast;\n})<\/code><\/pre>\n<p>With this modified code, the <code>MapGet<\/code> method now accepts an optional <code>days<\/code> parameter with a default value of <code>5<\/code>. So while the same default behavior exists, we&#8217;re exposing the parameter to consumers. The <code>days<\/code> parameter can be passed to the API as a query string. For example, consider the following HTTP request that asks for a 21-day weather forecast:<\/p>\n<pre><code class=\"language-http\">GET \/weatherforecast?days=21 HTTP\/1.1\nHost: localhost:7240\nScheme: https<\/code><\/pre>\n<p>This default value is used when the <code>days<\/code> parameter is not provided from the query string. The <code>days<\/code> parameter is used to specify the number of days for which the weather forecast should be generated. For more information on the ASP.NET Core Minimal APIs, see <a href=\"https:\/\/learn.microsoft.com\/aspnet\/core\/fundamentals\/minimal-apis\/parameter-binding#optional-parameters\">optional parameters<\/a>.<\/p>\n<h2>Next steps \ud83d\ude80<\/h2>\n<p>That&#8217;s a wrap for this four-part series on C# 12 features! I hoped you enjoyed learning about these new features and how they can help you refactor your code.<\/p>\n<p>In this post you learned about the default lambda parameters feature in C# 12. This feature allows developers to express default parameter values in lambdas. Be sure to try this out in your own code! For additional resources, I encourage you to check out the following links:<\/p>\n<ul>\n<li><a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/language-reference\/operators\/lambda-expressions\">C# Langauge Reference: Lambda expressions<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/language-reference\/proposals\/csharp-12.0\/lambda-method-group-defaults\">C# Langauge Reference: Lambda method group defaults<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Explore opportunities to refactor your C# code with default lambda parameters, a new feature in C# 12.<\/p>\n","protected":false},"author":24662,"featured_media":52246,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,756],"tags":[7701,7727],"class_list":["post-52245","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-csharp","tag-dotnet-8","tag-c-12"],"acf":[],"blog_post_summary":"<p>Explore opportunities to refactor your C# code with default lambda parameters, a new feature in C# 12.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/52245","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/24662"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=52245"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/52245\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/52246"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=52245"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=52245"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=52245"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}