.NET9 - 新功能體驗(三)

IT规划师發表於2024-11-24

書接上回,我們繼續來聊聊.NET9和C#13帶來的新變化。

01、Linq新方法 CountBy 和 AggregateBy

引入了新的方法 CountBy 和 AggregateBy後,可以在不經過GroupBy 分配中間分組的情況下快速完成複雜的聚合操作,同時方法命名也非常直觀,可以大大提升工作效率。

我們先以CountBy為例,簡單實現一個小功能,統計不同年齡有多少人,程式碼如下:

public class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
}
public void CountByExample()
{
    var students = new List<Student>
    {
        new Student { Name = "小明", Age = 10 },
        new Student { Name = "小紅", Age = 12 },
        new Student { Name = "小華", Age = 10 },
        new Student { Name = "小亮", Age = 11 }
    };
    //統計不同年齡有多少人,兩個版本實現
    //.NET 9 之前
    var group = students.GroupBy(x => x.Age);
    foreach (var item in group)
    {
        Console.WriteLine($"年齡為:{item.Key},有:{item.Count()} 人。");
    }
    //.NET 9
    foreach (var student in students.CountBy(c => c.Age))
    {
        Console.WriteLine($"年齡為:{student.Key},有:{student.Value} 人。");
    }
}

透過程式碼可以發現,老版本中必須先呼叫GroupBy分組再呼叫Count統計才可完成,而現在只需要呼叫CountBy即可。

我們再以AggregateBy為例子,看看新老版本中如何計算每個班級中各自學生總年齡,程式碼如下:

public class Student
{
    public string Name { get; set; }
    public string Grade { get; set; }
    public int Age { get; set; }        
}
public void AggregateByExample()
{
    var students = new List<Student>
    {
        new Student { Name = "小明", Grade = "一班", Age = 10 },
        new Student { Name = "小紅", Grade = "二班", Age = 12 },
        new Student { Name = "小華", Grade = "一班", Age = 10 },
        new Student { Name = "小亮", Grade = "二班", Age = 11 }
    };
    //統計每個班級各自學生總年齡,兩個版本實現
    //.NET 9 之前
    var old = students
       .GroupBy(stu => stu.Grade)
       .ToDictionary(group => group.Key, group => group.Sum(stu => stu.Age))
       .AsEnumerable();
    foreach (var item in old)
    {
        Console.WriteLine($"班級:{item.Key},總年齡:{item.Value} 。");
    }
    //.NET 9
    foreach (var group in students.AggregateBy(c => c.Grade, 0, (acc, stu) => acc + stu.Age))
    {
        Console.WriteLine($"班級:{group.Key},總年齡:{group.Value} 。");
    }
}

02、序列化加強

在System.Text.Json中,.NET 9為序列化提供了新的選項和一個新的單例。

1、縮排選項

現在可以透過JsonSerializerOptions新屬性IndentCharacter和IndentSize,自定義寫入 JSON 的縮排字元和縮排大小。看看下面程式碼。

static void Main()
{
    var options = new JsonSerializerOptions
    {
        WriteIndented = true,
        IndentCharacter = '\t',
        IndentSize = 2,
        //處理中文亂碼
        Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
    };
    var json = JsonSerializer.Serialize(
        new Student { Name = "小明", Grade = "一班", Age = 10 },
        options
    );
    Console.WriteLine(json);
    Console.ReadKey();
}

程式碼執行效果如下:

2、預設 Web 選項單例

在之前的版本中如果想要以小駝峰命名規則序列化物件可以配合JsonProperty特性實現。現在則可以直接透過JsonSerializerOptions.Web單例直接實現。

var json = JsonSerializer.Serialize(
    new Student { Name = "xiaoming", Grade = "yinianji", Age = 10 },
    JsonSerializerOptions.Web
);
Console.WriteLine(json);

程式碼執行效果如下:

03、Task新方法Task.WhenEach

在.NET9之前,如果我們有一個任務列表,並希望每個任務完成後立刻處理它,那麼我們只能透過不停的呼叫Task.WaitAny()方法來實現,現在.NET9引入了Task.WhenEach方法,以一種更優雅、更高效的方式處理這種情況。

因為Task.WhenEach 返回 IAsyncEnumerable<Task>,因此可以配合await foreach語句在任務完成時對其進行迭代,下面我們一起看看示例。

async Task WhenEachAsync()
{
    //生成100個隨機時間完成的任務列表
    var tasks = Enumerable.Range(1, 100)
                   .Select(async i =>
                   {
                       await Task.Delay(new Random().Next(1000, 5000));
                       return $"任務 {i} 完成";
                   })
                   .ToList();
    //.NET 9 之前
    while (tasks.Count > 0)
    {
        var completedTask = await Task.WhenAny(tasks);
        tasks.Remove(completedTask);
        Console.WriteLine(await completedTask);
    }
    //.NET 9
    await foreach (var completedTask in Task.WhenEach(tasks))
    {
        Console.WriteLine(await completedTask);
    }
}

04、新的 TimeSpan.From* 過載

在.NET9之前TimeSpan類提供了幾種From*方法,可以使用double型別來建立TimeSpan物件。但是,由於double是基於二進位制的浮點格式,因此固有的不精確性可能會導致錯誤。

為了解決這個問題,.NET 9 新增了新的過載方法,可以使用整數建立TimeSpan物件。

如下面這段程式碼:

TimeSpan timeSpan1 = TimeSpan.FromSeconds(value: 101.832);
Console.WriteLine($"timeSpan1 = {timeSpan1}");
// timeSpan1 = 00:01:41.8319999
TimeSpan timeSpan2 = TimeSpan.FromSeconds(seconds: 101, milliseconds: 832);
Console.WriteLine($"timeSpan2 = {timeSpan2}");
// timeSpan2 = 00:01:41.8320000

05、新的內建Swagger

從.NET9開始使用Scalar代替內建的Swagger(Swashbuckle),一方面是因為Swashbuckle專案維護不夠積極,另一個方面也是內部希望更專業於OpenAPI的發展。不管原因如何,我們都要根據自己的情況做好選擇。

下面我們來一起體驗一下Scalar。

首先建立一個Web Api專案,然後安裝Scalar.AspNetCore包,修改Prag程式碼如:

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddControllers();
    builder.Services.AddOpenApi();
    var app = builder.Build();
    // scalar/v1
    app.MapScalarApiReference(); 
    app.MapOpenApi();
    app.UseAuthorization();
    app.MapControllers();
    app.Run();
}

然後我們新增一個簡單的控制器,新增增刪改查四個方法,程式碼如下:

[ApiController]
[Route("[controller]")]
public class OrdersController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };
    private readonly ILogger<OrdersController> _logger;
    public OrdersController(ILogger<OrdersController> logger)
    {
        _logger = logger;
    }
    [HttpGet(Name = "")]
    public IEnumerable<Order> Get()
    {
        return Enumerable.Range(1, 5).Select(index => new Order
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Price = Random.Shared.Next(-20, 55),
            Name = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }
    [HttpPost(Name = "")]
    public bool Post(Order order)
    {
        return true;
    }
    [HttpPut(Name = "{id}")]
    public bool Put(string id, Order order)
    {
        return true;
    }
    [HttpDelete(Name = "{id}")]
    public bool Delete(string id)
    {
        return true;
    }
}

然後我們允許程式碼看看效果:

賣相還是相當驚豔的,左側是介面列表,左下角可以切換黑白兩種風格主題,右側是介面詳情,同時還配備了多種語言請求格式。

我們點選右下角Test Request測試一下獲取介面。

可以在左邊填寫好引數,新增最上面的Send,會看到右下角請求結果。更詳細複雜的應用大家可以自己摸索摸索。

:測試方法程式碼以及示例原始碼都已經上傳至程式碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner

相關文章