Mỗi đặc trưng có thể được dùng cho mục đích nào đó, nhưng tất cả chúng được dùng để tạo LINQ. Các đặc trưng gồm:
- Bộ khởi tạo đối tượng (object initializer)
- Khai báo biến cục bộ kiểu ngầm định (implicit types)
- Kiểu nặc danh (anonymous type)
- Biểu thức lambda (lambda expression)
- Phương thức mở rộng (extension method)
Phần này sẽ cung cấp thông tin chi tiết các đặc trưng và là phần rất quan trọng để hiểu các ví dụ trong các phần hay chương tiếp theo.
Bộ khởi tạo đối tượng (Object Initializers)
Chúng ta có thể dùng bộ khởi tạo đối tượng để khởi tạo bất kỳ hay tất cả các thuộc tính của đối tượng trong cùng một lệnh thể hiện đối tượng nhưng không cần viết hàm constructor tuỳ chọn. Xét ví dụ đối tượng Car có 5 thuộc tính tự động mà không thông qua bất cứ hàm constructor nào.
Mã VB
Public Class Car Public Property VIN() As String Public Property Make() As String Public Property Model() As String Public Property Year() As Integer Public Property Color() As String End Class
Mã C#
public class Car { public string VIN { get; set; } public string Make { get; set; } public string Model { get; set; } public int Year { get; set; } public string Color { get; set; } }
Tạo một thể hiện và gán giá trị cho các thuộc tính như mã sau:
Mã VB
Dim c As New Car() c.VIN = "ABC123" c.Make = "Ford" c.Model = "F-250" c.Year = 2000
Mã C#
Car c = new Car(); c.VIN = "ABC123"; c.Make = "Ford"; c.Model = "F-250"; c.Year = 2000;
chúng ta cũng có thể khởi tạo giá trị cho các thuộc tính trong một lệnh như sau:
Mã VB
Dim c As New Car() With {.VIN = "ABC123", .Make = "Ford", .Model = "F-250", .Year = 2000}
Mã C#
Car c = new Car() { VIN = "ABC123", Make = "Ford", Model = "F-250", Year = 2000 };
Bộ khởi tạo đối tượng có một hình thức khác là bộ khởi tạo tập hợp (collection initializer) dành cho các tập dữ liệu hay tập đối tượng. Đoạn mã sau kết hợp bộ khởi tạo đối tượng và bộ khởi tạo tập hợp dùng để tạo một tập các đối tượng Car gồm 5 đối tượng Car và mỗi đối tượng có giá trị các thuộc tính khác nhau:
Mã VB
Private Function GetCars() As List(Of Car) Return New List(Of Car) From { New Car() With {.VIN = "ABC123", .Make = "Ford", .Model = "F-250", .Year = 2000}, New Car() With {.VIN = "DEF123", .Make = "BMW", .Model = "Z-3", .Year = 2005}, New Car() With {.VIN = "ABC456", .Make = "Audi", .Model = "TT", .Year = 2008}, New Car() With {.VIN = "HIJ123", .Make = "VW", .Model = "Bug", .Year = 1956}, New Car() With {.VIN = "DEF456", .Make = "Ford", .Model = "F-150", .Year = 1998} } End Function
Mã C#
private List<Car> GetCars() { return new List<Car> { new Car {VIN = "ABC123",Make = "Ford", Model = "F-250", Year = 2000}, new Car {VIN = "DEF123",Make = "BMW", Model = "Z-3", Year = 2005}, new Car {VIN = "ABC456",Make = "Audi", Model = "TT", Year = 2008}, new Car {VIN = "HIJ123",Make = "VW", Model = "Bug", Year = 1956}, new Car {VIN = "DEF456",Make = "Ford", Model = "F-150", Year = 1998} }; }
Chúng ta có thể dùng bộ khởi tạo đối tượng trong LINQ thông qua phép chiếu hay chọn (toán tử Select). Ví dụ đoạn mã sau sẽ lấy một danh sách các tên màu có chiều dài là 5 kí tự và sắp xếp tăng dần:
Mã VB
Dim colors() = { "Red", "Brown", "Orange", "Yellow", "Black", "Green", "White", "Violet", "Blue" } Dim fords As IEnumerable(Of Car) = From c In colors Where c.Length = 5 Order By c Select New Car() With {.Make = "Ford", .Color = c}
Mã C#
string[] colors = { "Red", "Brown", "Orange", "Yellow", "Black", "Green", "White", "Violet", "Blue" }; IEnumerable<Car> fords = from c in colors where c.Length == 5 orderby c select new Car() { Make = "Ford", Color = c };
Khái báo biến cục bộ kiểu ngầm định
.NET hỗ trợ khai báo kiểu ngầm định theo một số luật sau:
- Chỉ thực hiện cho các biến cục bộ (local variable)
- Lệnh khai báo phải có dấu bằng và phần bên phải phải khác null
- Không thực thi trên các tham số phương thức.
Ví dụ thay vì viết:
Mã VB
Dim cars As List(Of Car) = GetCars()
Mã C#
List<Car> cars = GetCars();
Chúng ta có thể viết theo kiểu ngầm định:
Mã VB
Dim cars = GetCars()
Mã C#
var cars = GetCars();
Đặc trưng này quan trọng trong LINQ vì LINQ sử dụng các kiểu nặc danh (anonymous types) được hỗ trợ bởi đặc trưng khai báo kiểu ngầm định.
Kiểu nặc danh (anonymous types)
Trong một vài trường hợp chúng ta muốn gộp một vài dữ liệu trong một hình thức tạm thời, nghĩa là không cần khai báo một kiểu mới. Ví dụ tạo một biến x chứa hai thuộc tính Make và Model như sau:
Mã VB
Dim x = New With {.Make = "VW", .Model = "Bug"} txtLog.AppendText(x.Make & ", " & x.Model)
Mã C#
var x = new {Make = "VW", Model = "Bug"}; txtLog.AppendText(x.Make + ", " + x.Model);
kiểu của biến x là kiểu ngầm định và được hiểu tự động bởi trình biên dịch.
Các kiểu nặc danh được dùng trong LINQ để hỗ trợ phép chiếu hay chọn. Ví dụ sau tạo một truy vấn LINQ hiển thị một danh sách các Car với thuộc tính VIN và một thuộc tính kết hợp bởi hai thuộc tính Make và Model là MakeAndModel:
Mã VB
Dim carData = From c In GetCars() Where c.Year >= 2000 Order By c.Year Select New With { c.VIN, .MakeAndModel = c.Make + " " + c.Model } dgResults.DataSource = carData.ToList()
Mã C#
var carData = from c in GetCars() where c.Year >= 2000 orderby c.Year select new { c.VIN, MakeAndModel = c.Make + " " + c.Model }; dgResults.DataSource = carData.ToList();
Biểu thức lambda (lambda expression)
Các phương thức mở rộng (extension method)
Các phương thức mở rộng cho phép chúng ta thêm các phương thức đến một kiểu ngay cả khi kiểu này không có mã nguồn. Ví dụ chúng ta cần thêm phương thức isNumeric() đến lớp string nhưng chúng ta không biết mã nguồn của lớp string. Có 3 giải pháp cho vấn đề này:
Giải pháp thứ nhất là tạo một lớp mới, tạm gọi là MyString, thừa kế từ lớp string và thêm phương thức isNumeric vào lớp mới này. Vấn đề của giải pháp này là bạn phải đảm bảo lớp string cho phép thừa kế, phải đảm bảo người dùng sử dụng lớp MyString thay vì dùng string, và phải viết mã để chuyển các chuỗi khi nhận được.
Giải pháp thứ hai là tạo một lớp trợ giúp, tạm gọi là StringHelper, chứa tất cả các phương thức mà chúng ta cần thêm vào lớp string, trong trường hợp này là isNumeric. Các phương thức này là static và nhận lớp string như là tham số đầu tiên. Đoạn mã minh hoạ như sau:
Mã VB
Public Module StringHelper Public Function IsNumeric(ByVal str As String) As Boolean Dim val As Double Return Double.TryParse(str, val) End Function End Module
Mã C#
public static class StringHelper { public static bool IsNumeric(string str) { double val; return double.TryParse(str, out val); } }
Chúng ta có thể dùng phương thức isNumeric() thông qua lớp StringHelper như sau:
Mã VB
Dim s As String = "abc123" txtLog.AppendText(StringHelper.IsNumeric(s) & Environment.NewLine) s = "123" txtLog.AppendText(StringHelper.IsNumeric(s) & Environment.NewLine)
Mã C#
string s = "abc123"; txtLog.AppendText(StringHelper.IsNumeric(s) + Environment.NewLine); s = "123"; txtLog.AppendText(StringHelper.IsNumeric(s) + Environment.NewLine);
Ưu điểm của giải pháp thứ hai là không cần tạo ra một thể hiện cho lớp string để dùng phương thức isNumeric, nhược điểm là người dùng phải đến sự tồn tại của lớp StringHelper.
Giải pháp thứ ba là sử dụng các phương thức mở rộng được hỗ trợ bởi .NET Framework (kể từ 3.5): thêm một public module (trong VB) hay một static class (trong C#) và tạo một phương thức public static. Thêm thuộc tính <Extension()> trước phương thức nếu dùng VB và thêm từ khoá this đến tham số đầu tiên của phương thức nếu đang dùng C#. Đoạn mã như sau:
Mã VB
Imports System.Runtime.CompilerServices Public Module StringHelper <Extension()>Public Function IsNumeric(ByVal str As String) As Boolean Dim val As Double Return Double.TryParse(str, val) End Function End Module
Mã C#
public static class StringHelper { public static bool IsNumeric(this string str) { double val; return double.TryParse(str, out val); } }
Chúng ta có thể sử dụng phương thức isNumeric như sau:
Mã VB
Dim s As String = "abc123" txtLog.AppendText(s.IsNumeric() & Environment.NewLine) s = "123" txtLog.AppendText(s.IsNumeric() & Environment.NewLine)
Mã C#
string s = "abc123"; txtLog.AppendText(s.IsNumeric() + Environment.NewLine); s = "123"; txtLog.AppendText(s.IsNumeric() + Environment.NewLine);
Một vài quy định khi dùng phương thức mở rộng:
- Phải được định nghĩa trong module (VB) hay static class (C#)
- Module hay static class phải là public
- Nếu bạn định nghĩa một phương thức cho một kiểu và kiểu này cũng đã có một phương thức tương tự thì phương thức mặc định của kiểu phải được ưu tiên dùng và phương thức mở rộng được bỏ qua.
- Trong C#, lớp và phương thức mở rộng phải là static. Trong VB, module và phương thức mở rộng là static tự động (Shared).
- Không thể dùng phương thức mở rộng để thêm các phương thức static vào kiểu.
- Cần khai báo các namespace (Imports hay using) khi dùng các phương thức mở rộng.
Các phương thức mở rộng truy vấn (Query extension methods)
Microsoft đã thêm các phương thức mở rộng đến một vài kiểu nhưng quan trọng nhất là các phương thức được thêm vào giao diện IEnumerable. Các phương thức mở rộng trong giao diện IEnumerable cũng được biết đến như là các phương thức mở rộng truy vấn được dùng trong LINQ. Các phương thức này chúng ta sẽ đề cập cùng các ví dụ trong Các toán tử truy vấn.
Ý kiến bài viết