Sách Microsoft miễn phí "Introducing Microsoft LINQ" - Chương 3.3: Các tính năng của Visual Basic 9.0 không liên quan đến C# 3.0

XML Support (Hỗ trợ XML)

Visual Basic 9.0 là ngôn ngữ thực hiện hỗ trợ cú pháp đặc biệt cho LINQ to XML, bao gồm XML Literals và Late Binding cho XML. Để minh họa điều này, chúng ta sẽ sử dụng một số lớp là một phần của LINQ to XML: XDocument, XElementXAttribute. Trong Chương 6, chúng ta sẽ tìm hiểu chi tiết hơn về các lớp này. Để mô tả hỗ trợ XML, chỉ cần biết các lớp này đại diện cho tài liệu XML, phần tử và thuộc tính là đủ.

XML Literals (Biểu thức XML)

Trong Visual Basic 9.0, XML Literal được coi là một biểu thức. Nếu bạn muốn gán một giá trị cho một đối tượng đại diện cho cây XML, hãy viết mã như trong Listing 3-23:

Dim sachCuaChungToi As XElement
sachCuaChungToi = _
    <Sach TenSach="Giới thiệu LINQ">
        <TacGia>Marco Russo</TacGia>
        <TacGia>Paolo Pialorsi</TacGia>
    </Sach>

Chúng ta đã gán một thể hiện của XElement có tên là Sach cho biến sachCuaChungToi, phần tử này có một thuộc tính TenSach (giá trị là "Giới thiệu LINQ") và hai phần tử TacGia. Trình biên dịch sẽ dịch mã trong Listing 3-23 thành các cuộc gọi sau:

Dim sach As XElement
sach = New XElement("Sach", _
    New XAttribute("TenSach", "Giới thiệu LINQ"), _
    New XElement("TacGia", "Marco Russo"), _
    New XElement("TacGia", "Paolo Pialorsi"))

Quan trọng: Như chúng ta đã nói trước đây, XML Literals trong Visual Basic 9.0 là biểu thức. Khi định nghĩa nhiều dòng các biểu thức này, không cần ký tự tiếp tục dòng. Quan trọng hơn, cả dấu gạch dưới và bình luận đều được coi là một phần của XML Literals, thay vì là ký tự tiếp tục dòng hoặc bình luận XML. Đây là một ngoại lệ lớn đối với Visual Basic. Biểu thức kết thúc bằng một thẻ đóng tương ứng với thẻ ban đầu.

Thông thường, chúng ta suy ra kiểu của biến bằng cách đặt nhiệm vụ ban đầu vào trong khai báo. Trong nhiệm vụ sau, chúng ta cũng có thể thấy định nghĩa XML Literals trên một dòng:

Dim sach = <Sach TenSach="Giới thiệu LINQ"></Sach>

Hiểu rằng XML Literals được trình biên dịch chuyển đổi thành các cuộc gọi phương thức là rất quan trọng. Trình biên dịch yêu cầu cú pháp XML hợp lệ. XML Literals không hợp lệ sẽ gây ra lỗi biên dịch. Trong Listing 3-24, XML Literals được gán cho biến a có thuộc tính tenSach không hợp lệ (thiếu một dấu "), và biến b thiếu một thẻ đóng (</Sach>):

Dim a = <Sach TenSach="TenSach không hợp lệ"></Sach> ' Lỗi
Dim b = <Sach TenSach="TenSach tốt"><Sach>    ' Lỗi

Visual Basic 9.0 ảnh hưởng đến XML Literals, vì vậy nó cho phép gọi các biểu thức khác. Điều này có nghĩa là XML Literals có thể được tính toán tại thời điểm chạy thay vì chỉ là hằng số trong mã. Ví dụ, giả sử chúng ta muốn tạo động XML Literals cho một cuốn sách và chỉ định tên sách bằng một chuỗi. Trong trường hợp này, chúng ta cần sử dụng các thẻ <%= và %> phổ biến trong Visual Basic 9.0 để "ngắt" XML Literals. Listing 3-25 minh họa cách sử dụng các biểu thức phổ biến của Visual Basic 9.0 để gán thuộc tính và phần tử:

Dim tenSach = "Giới thiệu LINQ"  
Dim tacGia1 = "Marco Russo"  
Dim tacGia2 = "Paolo Pialorsi"  
Dim sach = _  
<Sach TenSach=<%= tenSach %>>  
<TacGia><%= tacGia1 %></TacGia>  
<TacGia><%= tacGia2 %></TacGia>  
</Sach>

Cảnh báo: Sau <%= và trước %> hãy để một khoảng trắng, nếu không trình biên dịch sẽ không hiểu đúng các biểu thức.

Thẻ <%= và %> định nghĩa một "khoảng trống" cho XML Literals để lồng các biểu thức, tính toán giá trị của biểu thức tại thời điểm chạy và thay thế "khoảng trống" đó. Các "khoảng trống" này tương đương với các trình khởi tạo XElement, XAttribute hoặc XDocument. Chúng ta có thể đặt bất kỳ biểu thức hợp lệ trong ngữ cảnh nào vào "khoảng trống".

Dim tenSach = "Giới thiệu LINQ"  
Dim tacGia1 = "Marco Russo"  
Dim tacGia2 = "Paolo Pialorsi"  
Dim theTagSach = "Sach"  
Dim tenThuocTinh = "TenSach"  
Dim theTagTacGia = "TacGia"  
Dim sach = _  
<<%= theTagSach %> <%= tenThuocTinh %>=<%= tenSach %>>  
<<%= theTagTacGia %>><%= tacGia1 & ", " & tacGia2 %></>  
</>

Tôi nghĩ rằng mã trong Listing 3-26 không phải là cách tốt nhất để sử dụng XML Literals. Việc chèn mã bằng dấu ngoặc nhọn và "khoảng trống" vào mã có thể ảnh hưởng đến khả năng đọc mã. Cách định nghĩa sach trong Listing 3-27 có thể rõ ràng hơn (để đơn giản, chúng ta bỏ qua phần khai báo biến).

Dim sach = _  
    New XElement(theTagSach, _  
        New XAttribute(tenThuocTinh, tenSach), _  
        New XElement(theTagTacGia, tacGia1 & ", " & tacGia2))

Mẹo: Chúng tôi không nói rằng bạn nên sử dụng biểu thức để chỉ định tên thẻ trong XML Literals. Lý do nhấn mạnh điều này là để nhấn mạnh rằng các biểu thức lồng trong XML Literals được hiểu ngay lập tức khi tạo cấu trúc XML. Tóm lại, chúng ta có thể định nghĩa XML Literals mà không cần các hằng số nội bộ, nhưng chúng tôi cho rằng điều này không cải thiện đáng kể khả năng đọc so với các cuộc gọi phương thức thông thường.

Nếu sử dụng các biểu thức nhúng để định nghĩa tên phần tử XML, chúng ta phải chú ý đến thẻ đóng. Trong trường hợp này, thẻ đóng không thể có tên thẻ. Trong Listing 3-28, thẻ đóng chỉ là </>, không có tên thẻ được định nghĩa bởi biến theTagPhanTu.

Dim theTagPhanTu = "MoTa"
Dim mau = <<%= theTagPhanTu %>Mau phan tu</>

Trong XML Literals, chỉ có thể lồng một biểu thức. Vì XML Literals là một biểu thức, chúng ta có thể viết mã như sau:

Dim sach = _
    <Sach TenSach="Giới thiệu LINQ">
        <%= <NhaXuatBan>Microsoft Press</NhaXuatBan> %>
    </Sach>

Trong trường hợp này, phần tử NhaXuatBan là một XML Literals được lồng vào phần tử Sach bên ngoài, là một XML Literals khác. Ví dụ này hữu ích để quan sát chi tiết của một cú pháp quan trọng. Thẻ <%= phải ở cùng dòng với biểu thức tính toán. Thẻ %> và cuối của biểu thức tính toán phải ở cùng dòng. Ngược lại, mã sau là không hợp lệ:

Dim sach = _
    <Sach TenSach="Giới thiệu LINQ">
        <%= 
            <NhaXuatBan>Microsoft Press</NhaXuatBan>
        %>
    </Sach>

Chúng ta có thể sử dụng ký tự tiếp tục dòng để viết mã trên các dòng khác nhau như trong ví dụ sau. Lưu ý rằng ký tự tiếp tục dòng luôn ở ngoài XML Literals:

Dim sach = _
    <Sach TenSach="Giới thiệu LINQ">
        <%= _
            <NhaXuatBan>Microsoft Press</NhaXuatBan> _
        %>
    </Sach>

Nếu bạn muốn sử dụng nhiều biểu thức hơn trong một "khoảng trống", chúng ta phải định nghĩa một mảng các phần tử. Ví dụ, để đặt hai phần tử TacGia riêng biệt vào một "khoảng trống", chúng ta có thể bao quanh XML Literals bằng dấu ngoặc nhọn như trong mã của Listing 3-29. Lưu ý ký tự tiếp tục dòng ở cuối phần tử đầu tiên trong bộ khởi tạo mảng:

Dim sach = _
    <Sach TenSach="Giới thiệu LINQ">
    <%= { <TacGia>Marco Russo</TacGia>, _
         <TacGia>Paolo Pialorsi</TacGia> } %>
    </Sach>

Chúng ta vừa sử dụng một mảng để đặt danh sách các phần tử TacGia vào một phần tử Sach. Vì vậy, chúng ta cũng có thể đặt các phương thức khác tạo ra đối tượng IEnumerable vào các phần tử XML khác.

Trong khai báo được hiển thị trong Listing 3-30, chúng ta đã sử dụng truy vấn LINQ để lấy danh sách tác giả từ một mảng người. Như bạn có thể thấy, XML Literals có thể được nhúng vào một biểu thức truy vấn trả về một chuỗi XML Literals, chuỗi này được tạo bởi các biểu thức nhúng khác tham chiếu đến kết quả truy vấn.

Dim doi = {New With {.Ten = "Marco Russo", .VaiTro = "TacGia"}, _
           New With {.Ten = "Paolo Pialorsi", .VaiTro = "TacGia"}, _
           New With {.Ten = "Roberto Brunetti", .VaiTro = "NhanVienDanhGia"}}  
Dim sach = _  
    <Sach TenSach="Giới thiệu LINQ">
    <%= From nguoi In doi _
        Where nguoi.VaiTro = "TacGia" _
        Select <TacGia><%= nguoi.Ten %></TacGia> %>
    </Sach>

Bằng cách kết hợp cú pháp LINQ và XML Literals, chúng ta có thể xây dựng các cấu trúc dữ liệu XML đơn giản và phức tạp bao gồm kết quả truy vấn một cách đơn giản và hiệu quả.

Late Binding over XML (Ràng buộc muộn trên XML)

Khi chúng ta muốn truy cập dữ liệu XML, có thể cần phải điều hướng trước đến một cây đối tượng đại diện cho cấu trúc phân cấp của tài liệu XML. VB9 cung cấp một số cú pháp đơn giản hóa thao tác này, cú pháp này là "Late Binding over XML".

Bắt đầu với một danh sách XML của các bộ phim. Mỗi mục phim phải có thuộc tính TenPhim và một phần tử DaiDien, cùng với danh sách thể loại. Mã sau đây cho thấy một phần của mã khởi tạo trong ví dụ của chúng ta:

Dim phim = _
<Phim>
    <Phim TenPhim="Fight Club">
        <TheLoai>Tội phạm</TheLoai>
        <TheLoai>Kịch</TheLoai>
        <TheLoai>Hồi hộp</TheLoai>
        <DaiDien>David Fincher</DaiDien>
    </Phim>
    <!--các phim khác không được hiển thị ở đây-->
</Phim>

Cú pháp XML đầu tiên chúng ta hiển thị là child axis. Nếu chúng ta viết phim.<Phim>, chúng ta sẽ nhận được tất cả các thể loại của phim được chọn. Như trong Listing 3-31:

Dim fightClub = _
    (From phim In phim.<Phim> _
     Where phim.@TenPhim = "Fight Club" _
     Select phim).First()
     
' Lấy thể loại đầu tiên
Dim theLoaiDauTien = fightClub.<TheLoai>(0).Value
' Tương đương với: theLoaiDauTien = fightClub.Elements("TheLoai")(0).Value
Console.WriteLine("Đầu tiên = {0}", theLoaiDauTien)
     
' Hiển thị tất cả các thể loại
' Tương đương với: fightClub.Elements("TheLoai")
For Each g In fightClub.<TheLoai>
    Console.WriteLine(g.Value)
Next

Nếu truy vấn chỉ cung cấp một dòng, hoặc chúng ta chỉ quan tâm đến dòng đầu tiên của kết quả truy vấn, thì có thể truy cập phần tử đầu tiên của tập hợp. Ví dụ, fightClub.<TheLoai>(0) cho phép truy cập một thể hiện XElement liên quan. Nếu chúng ta cần giá trị hoặc thuộc tính của nó, chúng ta cần đọc thuộc tính Value của nó. Tuy nhiên, cú pháp này có thể trả về nhiều hơn một dòng. Trong trường hợp này, lặp qua tất cả các dòng có thể truy cập được chúng.

Cú pháp child axis được dịch thành cuộc gọi Element, chỉ định tên phần tử làm tham số. Thông tin chi tiết về cú pháp này, vui lòng tham khảo bình luận mã trong Listing 3-31. Thuộc tính có thể được truy cập thông qua attribute axis. fightClub.@TenPhim có thể lấy giá trị của thuộc tính. Như trong Listing 3-32:

' Hiển thị thuộc tính TenPhim của phim Fight Club
Console.WriteLine(fightClub.@TenPhim)
' Tương đương với: Console.WriteLine(fightClub.Attribute("TenPhim").Value)

Trục thuộc tính được dịch thành cuộc gọi Attribute, chỉ định tên thuộc tính làm tham số. Hoạt động trước là "descendants axis". Nó cho phép bạn lấy tất cả các phần tử con của một phần tử, bất kể vị trí của chúng trong cấu trúc phân cấp. Mã trong Listing 3-33 tương tự như mã trên, nhưng ở đây có ba dấu chấm thay vì một:

' Danh sách các đạo diễn
Dim daiDiens = phim<DaiDien>
For Each daiDien In daiDiens
    Console.WriteLine(daiDien)
Next

Trong trường hợp này, kết quả đầu ra của Listing 3-33 cho thấy các bản sao có thể có. Sử dụng toán tử Distinct có thể "làm sạch" đầu ra thừa. Chúng ta sẽ thảo luận thêm về điều này trong Chương 4.

Chúng ta sẽ phân tích sâu hơn về tích hợp XML và trình biên dịch .NET trong Chương 6.

Relaxed Delegates (Đại biểu linh hoạt)

Trong VB 8.0 và các phiên bản trước đó, nếu chúng ta muốn liên kết một phương thức với một đại biểu, hai chữ ký (phương thức và đại biểu) phải hoàn toàn giống nhau. Trong C#, phương thức chỉ cần một chữ ký "hợp lệ". Ví dụ, các loại cụ thể ít hơn trong tham số cũng được phép. Visual Basic 9.0 đã loại bỏ các hạn chế trước đây này, giờ đây chúng ta có thể viết mã như trong Listing 3-34:

Public Delegate Sub SuKienXuLy()

Public Class LopSuKien
    Public Event Click As SuKienXuLy
End Class

Module ChuongTrinh
    Public WithEvents A As LopSuKien
    
    Sub XuLyClick(ByVal doiTuong As Object, ByVal suKien As Object) Handles A.Click
        Console.WriteLine("Xin chào thế giới")
    End Sub
End Module

Trong Listing 3-34, mã có thể gây lỗi trong VB 8.0 đã được đánh dấu. Trong VB 8, để biên dịch thành công, chúng ta phải viết mã như sau (đã đánh dấu các tham số đã thay đổi):

Sub XuLyClick(ByVal doiTuong As Object, ByVal suKien As EventArgs) Handles A.Click

Nếu không sử dụng tham số bên trong phương thức được liên kết với đại biểu, chúng ta có thể bỏ qua toàn bộ danh sách tham số trong khai báo phương thức. Từ khóa Handles suy ra chữ ký thực sự của phương thức từ khai báo đại biểu tương ứng. Trong Listing 3-35, chúng ta có thể viết phương thức XuLyClick trước đó theo cách khác. C# không cho phép cú pháp này vì C# không có từ khóa tương ứng với Handles.

Sub XuLyClick2() Handles A.Click
    Console.WriteLine("Xin chào thế giới từ")
End Sub

Thẻ: Visual Basic 9.0 linq XML Literals Late Binding Relaxed Delegates

Đăng vào ngày 7 tháng 6 lúc 19:14