用默认 lambda 参数重构代码

Avatar
不若风吹尘
2024-06-20T17:31:07
200
0

在这个帖子中,我们将探讨 C# 12 中新增的 “默认 lambda 参数” 功能,让开发者能够在 lambda 表达式中直接指定默认值。

默认 lambda 参数 🧮

在 C# 12 之前,如果你需要在 lambda 表达式中提供某种默认行为,通常会使用 ??(null-coalescing 操作符)或 ?:(条件操作符)。看下面的例子:

var IncrementBy = static (int source, int? increment) => {
    // 等同于 source + (increment.HasValue ? increment.Value : 1)
    return source + (increment ?? 1);
};
Console.WriteLine(IncrementBy(5, null)); // 6
Console.WriteLine(IncrementBy(5, 2)); // 7

C# 12 之后 🤓

现在,有了默认 lambda 参数,你可以在 lambda 表达式中直接为参数定义默认值。默认参数的语法类似于方法中的默认参数。默认值紧跟在参数名后面,用等号(=)分隔。例如:

var IncrementBy = static (int source, int increment = 1) => {
    return source + increment;
};
Console.WriteLine(IncrementBy(10)); // 11
Console.WriteLine(IncrementBy(10, 20)); // 30

lambda 表达式的默认参数规则与方法相同:默认值必须是编译时常量,并且类型必须与参数一致。默认值在 编译时 计算,调用 lambda 表达式时参数是可选的。

delegate int (int arg1, int arg2 = 1);

这意味着当你在调用 lambda 时理论上可以只提供参数名称,但实际上是匿名函数生成的名称。比如,考虑下面扩展的例子:

var IncrementByWithOffset = static (int source, int increment = 1, int offset = 100) => {
    return source + increment + offset;
};
Console.WriteLine(IncrementByWithOffset(10)); // 111
Console.WriteLine(IncrementByWithOffset(10, 20)); // 130
Console.WriteLine(IncrementByWithOffset(10, 20, 0)); // 30
Console.WriteLine(IncrementByWithOffset(10, arg2: -100)); // 10
Console.WriteLine(IncrementByWithOffset(10, arg3: 0)); // 11

ASP.NET Core Minimal API 示例 🌐

让我们看一个使用默认 lambda 参数的 ASP.NET Core Minimal API 示例。在 Visual Studio 2022 中,通过文件 > 新建 > 项目 对话框创建一个新的ASP.NET Core Web API项目。或者,你可以使用以下.NET CLI 命令创建新项目:

dotnet new webapi -n WebApi

这个模板创建了一个带有单个 /weatherforecast 端点的 ASP.NET Core Web API 项目。/weatherforecast 端点返回五个随机天气预报,来看看模板代码中的 Program.cs 文件:

var builder = WebApplication.CreateBuilder(args);
// 添加服务到容器...
// 更多关于配置Swagger/OpenAPI的信息:https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();

// 配置HTTP请求管道...
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
app.MapGet("/weatherforecast", () => {
    var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast(
        DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
        Random.Shared.Next(-20, 55),
        summaries[Random.Shared.Next(summaries.Length)]
    )).ToArray();
    return forecast;
}).WithName("GetWeatherForecast").WithOpenApi();

app.Run();

这里有些模板代码,我们关注的重点是 MapGet 功能,因为它将我们的 lambda 功能映射到 HTTP GET 请求。

app.MapGet("/weatherforecast", () => {
    var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast(
        DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
        Random.Shared.Next(-20, 55),
        summaries[Random.Shared.Next(summaries.Length)]
    )).ToArray();
    return forecast;
}).WithName("GetWeatherForecast").WithOpenApi();

/weatherforecast 端点返回五个天气预报。Enumerable.Range(1, 5) 中的硬编码五可以替换为一个默认 lambda 参数,如下所示更新后的代码片段:

app.MapGet("/weatherforecast", (int days = 5) => {
    // 安全检查确保days参数至少为1,不超过50。
    var count = days > 0 && Enumerable.Range(1, count).Select(index => new WeatherForecast(
        DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
        Random.Shared.Next(-20, 55),
        summaries[Random.Shared.Next(summaries.Length)]
    )).ToArray();
    return forecast;
})

这样修改后,MapGet 方法现在接受一个可选的 days 参数,默认值为 5 。虽然默认行为保持不变,但我们向消费者暴露了这个参数。days 参数可以通过查询字符串传递给 API。例如,请求一个 21 天的天气预报的 HTTP 请求如下:

GET /weatherforecast?days=21 HTTP/1.1
Host: localhost:7240
Scheme: https

如果没有从查询字符串中提供 days 参数,就会使用这个默认值。days 参数用于指定应生成多少天的天气预报。

译自:https://devblogs.microsoft.com/dotnet/refactor-your-code-with-default-lambda-parameters/

Last Modification : 9/20/2024 4:39:02 AM


In This Document