Cú Pháp Thần Kỳ Trong C#: Những Phương Thức Đặc Biệt Mà Bạn Có Thể Chưa Biết

Giới Thiệu

Bài viết này là một bản cập nhật từ bài viết cũ trên blog cũ của tôi và được lấy cảm hứng từ các bài viết gần đây về C# so với Java.

Bạn có thể không nhận ra rằng một số mẫu thiết kế mà bạn đã sử dụng từ lâu trong các chương trình C# của mình thực chất dựa trên các quy ước, thay vì trên một API cụ thể. Có nghĩa là, một số cấu trúc trong C# dựa trên các phương thức “thần kỳ” với tên được xác định rõ ràng, không được định nghĩa trong bất kỳ lớp cơ sở hoặc giao diện nào, nhưng vẫn hoạt động bình thường. Hãy cùng xem chúng là gì.

Liệt Kê Bộ Sưu Tập

Bạn có lẽ đã quen với việc lặp qua các bộ sưu tập bằng câu lệnh foreach. Nếu có, bạn có thể biết rằng foreach thực chất bao bọc một lệnh gọi đến phương thức GetEnumerator, giống như phương thức được định nghĩa bởi các giao diện IEnumerableIEnumerable<T>. Tuy nhiên, bạn có thể nghĩ rằng phép thuật xảy ra vì bộ sưu tập triển khai một trong hai giao diện này, nhưng bạn đã sai: thực tế, để lặp qua một lớp bằng foreach, tất cả những gì cần thiết là lớp đó phải có một phương thức công khai có tên GetEnumerator trả về một đối tượng IEnumerator hoặc IEnumerator<T>. Ví dụ:

public class Enumerable{
    public IEnumerator GetEnumerator()
    {
        yield return 1;
        yield return 2;
        yield return 3;
    }
}
var e = new Enumerable();
foreach (int i in e) { /* 1, 2, 3 */ }

Phân Tách Thành Bộ

Bộ (tuples) được giới thiệu trong C# 7. Nói một cách đơn giản, chúng cung cấp cách để chúng ta trả về nhiều giá trị từ một phương thức:

(int x, int y) GetPosition() { return (x: 10, y: 20); }

Một tùy chọn khác là phân tách một lớp thành bộ. Ví dụ, nếu chúng ta có một lớp như thế này:

public class Rectangle{
    public int Height { get; set; }
    public int Width { get; set; }
}

Chúng ta có thể phân tách nó thành bộ bằng cách cung cấp một hoặc nhiều phương thức Deconstruct trong lớp này:

public void Deconstruct(out int h, out int w){
    h = this.Height;
    w = this.Width;
}

Điều này cho phép bạn viết mã như sau:

var rectangle = new Rectangle { Height = 10, Width = 20 };
var (h, w) = rectangle;

Bạn có thể triển khai nhiều phương thức Deconstruct với các tham số khác nhau, và chúng phải luôn là out. Khi bạn cố gắng gán lớp của mình cho một bộ, C# sẽ cố gắng tìm một phương thức Deconstruct phù hợp với khai báo của bộ, hoặc ném ra một ngoại lệ nếu không tìm thấy.

Khởi Tạo Bộ Sưu Tập

Từ C# 6, chúng ta có cú pháp ngắn gọn hơn để khởi tạo bộ sưu tập:

var strings = new List<string> { "A", "B", "C" };

Cú pháp này hoạt động vì có một phương thức công khai Add nhận một tham số có kiểu phù hợp. Điều xảy ra đằng sau là phương thức Add được gọi nhiều lần, một lần cho mỗi phần tử bên trong dấu ngoặc nhọn. Điều này có nghĩa là mã sau cũng hoạt động:

public class Collection : IEnumerable{
    public IEnumerator GetEnumerator() => /* ... */
    public void Add(string s) { /* ... */ }
}

Lưu ý rằng lớp này cung cấp một phương thức công khai Add nhưng vẫn cần triển khai IEnumerable hoặc IEnumerable<T>, điều mà, xin lưu ý, không định nghĩa phương thức Add.

Với điều này, chúng ta có thể viết:

var col = new Collection { "A", "B", "C" };

Phương thức Add thần kỳ có thể có nhiều tham số, giống như từ điển hoặc bất kỳ phương thức nào mà phương thức Add của nó nhận hai tham số.

Phương Thức Hủy

Đây là một tính năng cũ và có lẽ (hy vọng) mọi người đều biết về nó. Trong .NET, tất cả các lớp cuối cùng đều kế thừa từ Object, có nghĩa là tất cả chúng đều có một phương thức hủy, được gọi là Finalize. Thông thường, trong (các trường hợp hiếm) khi chúng ta cần ghi đè nó – chỉ trong các lớp, không phải trong cấu trúc – nó nên được gọi là ~MyClass.

public class MyClass{
    ~MyClass()
    {
        //thực hiện dọn dẹp
    }
}

Nạp Chồng Toán Tử

Trong C#, có thể nạp chồng (tôi muốn nói là ghi đè) một số toán tử và định nghĩa các toán tử chuyển đổi. Ví dụ, bạn có thể làm cho một lớp MyClass hoạt động như một bool, hoặc sửa đổi các toán tử bằng, không bằng.

Toán tử chuyển đổi có thể là tường minh hoặc ngầm định, có nghĩa là bạn có thể cần hoặc không cần cú pháp chuyển đổi.

Danh sách các toán tử có thể ghi đè bao gồm các toán tử đơn nguyên, toán tử nhị phân và toán tử so sánh. Khi bạn bắt đầu ghi đè các toán tử chuẩn, bạn có thể kết thúc với một cú pháp rất kỳ lạ, vì vậy tốt hơn hãy biết bạn đang làm gì!

Await Tùy Chỉnh

Bạn có lẽ đã quen với việc sử dụng await trên các đối tượng Task, nhưng, trong thực tế, bạn có thể ngạc nhiên khi biết rằng bạn có thể sử dụng await trên bất kỳ lớp nào cung cấp phương thức GetAwaiter với các đặc điểm sau:

Mẫu Truy Vấn Tùy Chỉnh

Bạn có biết rằng bạn có thể sử dụng cú pháp truy vấn C# trên bất kỳ lớp nào không? Chỉ cần triển khai các toán tử chuẩn như Cast, Where, Select, OrderBy, v.v.

Kết Luận

Như bạn có thể thấy, C# cung cấp một số cú pháp đặc biệt mà tất cả các nhà phát triển nên biết. Nó thậm chí cho phép chúng ta viết mã rất kỳ lạ, vì vậy tốt hơn hãy biết những gì chúng ta đang làm! Hãy cập nhật với các thay đổi ngôn ngữ C# gần đây!

Chỉ mục