Các thành viên static abstract trong interface C# 10

Ngôn ngữ C# đã bật các bộ tăng áp liên quan đến các tính năng ngôn ngữ, với những người yêu thích hoặc ghét các bổ sung. Thật công bằng khi có ý kiến, nhưng hôm nay tôi muốn cho bạn thấy một trong những tính năng mới yêu thích của tôi đối với ngôn ngữ C# và tại sao bạn nên quan tâm.

Các thành viên static abstract trong interface rất đáng chú ý từ việc sử dụng nó ngay lập tức trong các dự án của bạn và những tác động mà nó có thể có đối với các tác giả framework và người bảo trì dự án.

Vì vậy, hãy đi cùng tôi trong cuộc hành trình này, và trong khi bạn nhìn thấy những ví dụ này, hãy nhớ suy nghĩ về các trường hợp sử dụng của bạn.

Lưu ý: Bạn sẽ cần .NET 6 và thiết lập LangVersionpreviewtrong tệp ứng dụng web csproj của bạn.

Thành viên static abstract là gì?

Các nhà phát triển C# đã quen thuộc với việc khai báo interface, nhưng hãy để tôi giải thích cho những người chưa biết. Interface là một cơ chế mà bạn có thể định nghĩa một hợp đồng.

Toàn bộ các class triển khai interface, bằng cách này hay cách khác, phải thực hiện hợp đồng mà interface đã định nghĩa. Interface có thể bao gồm các phương thức, thuộc tính, chỉ mục và sự kiện.

Trong thời gian dài nhất, Bạn chỉ khai báo các interface không có triển khai. Trong C# 8, ngôn ngữ này đã giới thiệu cho chúng ta các thành viên static, cho phép chúng ta chia sẻ chức năng trên tất cả các trình triển khai interface.

Tính năng ngôn ngữ này làm giảm đáng kể việc triển khai cần thiết trong cơ sở mã của bạn, đặc biệt nếu cơ sở mã của bạn triển khai nhiều interface.

Các thành viên static abstract cho phép mỗi thành viên triển khai của interface triển khai phiên bản của trình truy cập tĩnh mà bạn có thể truy cập thông qua xử lý Type.

Bạn có thể triển khai các thành viên này một cách ngầm định hoặc rõ ràng, giống như bất kỳ định nghĩa interface nào khác. Hãy xem một ví dụ, vì nó cho thấy rõ ràng hơn về cách hoạt động của tất cả.

void HasSeeds<T>(T fruit) where T : IFruit 
{
    Console.WriteLine(T.HasSeeds);
}

HasSeeds(new Apple());
HasSeeds(new Watermelon());

public record Watermelon : IFruit
{
    public static bool HasSeeds => false;
}

public record Apple : IFruit
{
    public static bool HasSeeds => true;
}

public interface IFruit
{
    static abstract bool HasSeeds { get; }
}
Lưu ý cách bạn có thể truy cập thành viên static HasSeeds trong phương thức chung của chúng ta mà không cần biết chính xác cụ thể  kiểu của T là gì.

Thông thường, loại truy cập này sẽ chỉ có sẵn thông qua reflection. Như thường được biết, reflection có thể là một kẻ giết chết hiệu suất và là nguồn gốc của các ngoại lệ khi thực thi.

Như bạn có thể đoán được, chúng ta không gặp những vấn đề đó ở đây. Các thành viên static abstract cũng là một phương pháp tuyệt vời để thêm siêu dữ liệu chung về các kiểu của chúng ta trong khi thực thi mỗi người triển khai hoàn thành hợp đồng.

Một trường hợp sử dụng của thành viên static abstract

Hầu hết các framework ASP.NET Core hoạt động chủ yếu dựa trên reflection và tìm kiếm siêu dữ liệu thông qua các kiểu Attribute.

Tuy nhiên, reflection có thể thêm chi phí đáng kể cho thời gian khởi động và có thể dễ xảy ra lỗi, vì các nhà phát triển có thể quên thêm các điểm cuối với các thuộc tính chính xác. Hãy xem một ví dụ về điểm cuối MVC trực tiếp từ các mẫu ASP.NET Core.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet(Name = "GetWeatherForecast")]
    public IEnumerable<WeatherForecast> Get()
    {
        return Enumerable.Range(1, 5)
            .Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
    }
}

Như bạn có thể nhận thấy, ít nhất có các phần siêu dữ liệu trên controller này như: HttpGet, Route ApiController. Thật không may, ASP.NET Core cũng phải quét project của chúng ta và sử dụng reflection để xác định sự hiện diện của các thuộc tính này.

Ngoài ra, không có cách nào bắt buộc để người dùng sử dụng HttpGet hoặc Route. Việc quên thêm các thuộc tính này có thể dẫn đến hàng giờ gỡ lỗi khó chịu với ứng dụng của bạn có các hành vi như điểm cuối không thể truy cập, không được đưa vào đặc tả OpenAPI hoặc không thể tạo liên kết nội bộ.

Đã có một số khám phá trong Cộng đồng .NET về việc cung cấp mô hình lập trình web dựa trên điểm cuối. Tuy nhiên, ngay cả những người được tiếp cận hiện phải dùng đến reflection với siêu dữ liệu.

Vì vậy, hãy xem liệu chúng ta có thể xây dựng một phương pháp tiếp cận chặt chẽ hơn mà không cần reflection hay không, đồng thời đẩy người dùng của chúng ta vào BÍ QUYẾT THÀNH CÔNG! .

using static Microsoft.AspNetCore.Http.Results;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapHandler<HelloWorld>();
app.Run();

public interface IHandler
{
    static abstract string Template { get; }
    static abstract HttpMethod Method { get; }
    static abstract Delegate Handle { get; }
}

public record struct HelloWorld : IHandler
{
    public static HttpMethod Method => HttpMethod.Get;
    public static string Template => "/";
    public static Delegate Handle => 
        (HttpRequest _) => Ok("Hello, World!");
}

public static class ApplicationHandlerExtensions
{
    public static void MapHandler<THandler>(this WebApplication app)
        where THandler : IHandler
    {
        app.MapMethods(
            THandler.Template, 
            new[] { THandler.Method.ToString() }, 
            THandler.Handle );
    }
}

Chúng ta sẽ thấy “Hello, World!” từ phương thức Handle của chúng ta bằng cách chạy ứng dụng. Không phải là quá xa để tưởng tượng việc xây dựng một trình tạo nguồn truy xuất tất cả các triển khai IHandler và đăng ký các điểm cuối, có thể tất cả thông qua một lệnh gọi duy nhất MapHandlers.

Một lần nữa, không có reflection, tất cả đăng ký dựa trên thời gian biên dịch của các điểm cuối của chúng ta dựa trên các interface rất nghiêm ngặt.

Chúng ta có thể thấy những ưu điểm đáng kể nhất trong phương thức MapHandler, nơi chúng ta sử dụng THandler để truy cập các thành viên static.

public static class ApplicationHandlerExtensions
{
    public static void MapHandler<THandler>(this WebApplication app)
        where THandler : IHandler
    {
        app.MapMethods(
            THandler.Template, 
            new[] { THandler.Method.ToString() }, 
            THandler.Handle );
    }
}

Phần kết luận

Các thành viên static abstract cho phép chúng ta trao quyền tự quyết cho những class triển khai interface để làm những gì chúng muốn trong khi cung cấp cho chúng ta khả năng trích xuất siêu dữ liệu mà trước đây chỉ có thể thực hiện được thông qua reflection hoặc tạo mã.

Tôi hy vọng bài đăng này sẽ khơi dậy một số suy nghĩ thú vị về các khả năng trong codebase của bạn.

Lập Trình C#Tính năng mới của C#
Bài Viết Liên Quan:
Loạt bài: Khám phá .NET 6
Trung Nguyen 02/04/2022
Loạt bài: Khám phá .NET 6

Trong loạt bài này, tôi sẽ xem xét một số

Tạo tập tin Zip với .NET 5
Trung Nguyen 11/11/2021
Tạo tập tin Zip với .NET 5

Trong bài viết này, chúng ta sẽ tìm hiểu lớp tiện ích ZipFile trong C#, cách nén tập tin và thư mục, cùng với giải nén tập tin zip.

Đọc và ghi file Excel trong C#
Trung Nguyen 29/10/2021
Đọc và ghi file Excel trong C#

Bài viết này sẽ giới thiệu cách đơn giản nhất mà tôi đã tìm thấy để đọc và ghi file Excel bằng C# sử dụng ExcelMapper.

Làm việc với PriorityQueue của .NET 6
Trung Nguyen 25/10/2021
Làm việc với PriorityQueue của .NET 6

Bài viết này sẽ giúp bạn tìm hiểu PriorityQueue của .NET 6 là gì, cách chúng ta thêm các phần tử và cách chúng ta có thể xếp hàng lại cho các phần tử.