GT C Sharp cơ bản – Bài 3 : Chương 2: Môi trường thời gian chạy .NET
Chương 2: Môi trường thời gian chạy .NET
Tác giả: Sưu tầm
Khái quát
Trước đây, việc viết các module để có thể được gọi từ nhiều ngôn ngữ là khó khăn. Mã được viết trong Visual Basic thì không thể thực thi được từ Visual C++. Mã được viết bằng Visual C++ thì đôi khi có thể thực hiện được từ Visual Basic, nhưng không phải là dễ dàng. Visual C++ sử dụng những thời gian chạy C và C++, có hành vi rất đặc biệt, và Visual Basic sử dụng động cơ thi hành riêng của nó, cũng với những hành vi đặc biệt – và khác – của riêng nó.
Và như vậy COM được tạo ra, và nó đã khá thành công như một cách viết phần mềm dựa trên các thành phần. Nhưng không may, khá là khó để sử dụng trong Visual C++, và nó chưa hoàn toàn được đề cao trong Visual Basic. Và do đó, nó chỉ được sử dụng rộng rãi khi viết những thành phần COM, và ít thường xuyên khi viết các ứng dụng truyền thống. Như vậy, nếu một người lập trình đã viết một số đoạn mã tốt trong C++, và một người khác viết một số đoạn mã tốt khác trong Visual Basic, thì thật sự không có cách dễ dàng để làm việc cùng nhau dễ dàng.
Hơn nữa, thế giới là khó khăn trong việc cung cấp thư viện, như không có một sự lựa chọn nào sẽ làm việc được với tất cả các thị trường. Nếu người viết nghĩ rằng thư viện đó được nhắm tới những người lập trình Visual Basic, thì sẽ dễ dàng để được sử dụng từ Visual Basic, nhưng lựa chọn này có thể hoặc là ép buộc truy xuất từ viễn cảnh C++ hoặc đến với một sự bất lợi về thực thi không thể chấp nhận được. Hoặc, một thư viện có thể được viết bởi người sử dụng C++, để hiệu suất tốt và truy xuất ở cấp thấp, nhưng nó sẽ bỏ qua những người lập trình Visual Basic.
Đôi khi một thư viện được viết cho cả các kiểu của người sử dụng, nhưng điều này thường có nghĩa rằng có vài thoả hiệp. Để gửi email trong hệ thống Windows, có một sự lựa chọn giữa Collaboration Data Objects (CDO – các đối tượng dữ liệu cộng tác), một giao diện dựa trên COM mà nó có thể được gọi từ nhiều ngôn ngữ nhưng không làm mọi thứ, và những hàm MAPI gốc (trong cả phiên bản C và C++) mà có thể truy xuất tất cả các hàm.
Thời gian chạy .NET được thiết kế để khắc phục tình huống này. Có một cách cho việc mô tả mã (metadata), và một thời gian chạy và thư viện (Ngôn ngữ thời gian chạy chung và cơ cấu). Sơ đồ sau cho chúng ta thấy Thời gian chạy .NET được sắp xếp như thế nào:
Web Services |
User Interface |
Data và XML |
|
Base Classes |
|
|
|
Common Language Runtime |
Hình 2-1. Sơ đồ tổ chức .NET Frameworks
Common Language Runtime cung cấp những dịch vụ thực thi cơ bản. Trên đỉnh của nó, phần Base Classes cung cấp các kiểu dữ liệu cơ sở, các lớp sưu tập, và các lớp chung khác. Trên phần này là các lớp để xử lý dữ liệu và XML. Cuối cùng, đỉnh của kiến trúc này là các lớp phơi bày các dịch vụ web và xử lý giao diện người sử dụng. Một ứng dụng có thể gọi đến tại bất kỳ mức nào và sử dụng các lớp từ bất kỳ mức nào đó.
Để hiểu cách C# làm việc, thì là quan trọng để hiểu một chút về Thời gian chạy .NET và Frameworks. Phần tiếp theo cung cấp một khái quát; và các thông tin chi tiết hơn có thể được tìm thấy sau này ở Chương 31, “Sâu hơn với C#”.
Môi trường thực thi
Mục này một lần đã được đặt tên là “Động cơ thực thi”, nhưng thời gian chạy .NET còn hơn là một động cơ. Môi trường cung cấp một mô hình lập trình đơn giản hơn, an toàn và bảo mật, hỗ trợ các công cụ mạnh mẽ, và giúp đỡ việc phát triển, đóng gói và các hỗ trợ khác.
Một mô hình lập trình đơn giản hơn
Tất cả các dịch vụ được cung cấp thông qua một mô hình chung mà có thể được truy xuất một cách bằng nhau từ tất cả các ngôn ngữ .NET, và các dịch vụ có thể được viết bằng bất kỳ ngôn ngữ .NET nào. Môi trường là ngôn ngữ-bất khả tri rộng lớn, cho phép sự lựa chọn ngôn ngữ. Điều này làm cho việc sử dụng lại mã dễ dàng hơn, cả cho người lập trình và nhà cung cấp thư viện.
Môi trường cũng cung cấp khả năng sử dụng các mã hiện có bằng C#, hoặc là thông qua gọi các hàm DLL, hoặc làm cho các thành phần COM thành các thành phần thời gian chạy .NET. Các thành phần thời gian chạy .NET cũng có thể được sử dụng trong những tình huống mà các thành phần COM được đòi hỏi.
Ngược lại với các kỹ thuật xử lý lỗi khác nhau trong các thư viện hiện có, trong thời gian chạy .NET tất cả các lỗi đều được báo cáo thông qua những ngoại lệ. Không cần phải chuyển đổi giữa các mã lỗi, các HRESULT, và các ngoại lệ.
Cuối cùng, môi trường chứa đựng một thư viện lớp cơ sở (BCl – Base Class Libraries), mà nó cung cấp các hàm truyền thống được tìm thấy trong các thư viện thời gian chạy, cộng thêm một số hàm mới. Một số chức năng thư viện lớp cơ sở cung cấp bao gồm:
- Các lớp sưu tập, như hàng đợi, mảng, stack, và bảng băm
- Các lớp truy xuất cơ sở dữ liệu
- Các lớp IO
- Các lớp WinForm, để tạo giao diện người sử dụng
- Các lớp về mạng
Ngoài lớp cơ sở thời gian chạy, thì có nhiều thành phần khác xử lý UI và thực hiện các thao tác phức tạp khác.
An toàn và bảo mật
Môi trường thời gian chạy .NET được thiết kế là một môi trường an toàn và bảo mật. Thời gian chạy .NET là một môi trường có quản, có nghĩa là thời gian chạy quản lý bộ nhớ cho người lập trình. Thay vì phải quản lý việc định vị và thu hồi bộ nhớ, thì trình gom rác đã làm điều này. Trình gom rác không chỉ giảm số lượng công việc phải làm khi lập trình, mà trong một môi trường máy chủ nó còn có thể làm giảm một cách mạnh mẽ khả năng rò rỉ bộ nhớ. Điều này kiến các hệ thống có giá trị cao dễ dàng hơn để phát triển.
Ngoài ra, thời gian chạy .NET là một môi trường có kiểm chứng. Tại thời điểm thực thi, môi trường kiểm chứng xem mã thực thi có an toàn kiểu không. Nó có thể bắt các lỗi, như chuyển kiểu sai cho hàm, và các tấn công, như cố gắng đọc hơn ranh giới được xác định hay thực thi mã với một sự định vị tuỳ tiện.
Hệ thống bảo mật tương tác với trình kiểm chứng để bảo đảm rằng mã chỉ làm những gì mà nó được phép làm. Nhu cầu bảo mật cho một phần mã nhất định có thể được biểu thị theo cách kết hợp tinh tế; ví dụ, mã có thể chỉ định rằng nó cấn được viết một tập tin hỗn tạp, và yêu cầu này sẽ được kiểm tra trong suốt quá trình thực thi.
Hỗ trợ các công cụ mạnh mẽ
Microsoft cung cấp bốn ngôn ngữ .NET: Visual Basic, Visual C++ có quản, C#, và Jscript. Các công ty khác cũng đang tiếp tục viết các trình biên dịch cho các ngôn ngữ khác mà nó thực thi tất cả từ COBOL cho đến Perl.
Gở lỗi được tăng cường đáng kể trong thời gian chạy .NET. Mô hình thực thi chung dẫn đến việc gở lỗi ngôn ngữ chéo dễ dàng và đơn giản, và việc gở lỗi có thể mở rộng không biên giới cho các mã được viết bằng các ngôn ngữ khác nhau và thực thi trong các tiến trình khác nhau hoặc ở các thiết bị khác nhau.
Cuối cùng, tất cả các nhiệm vụ lập trình .NET là được gắn kết với nhau thông qua môi trường Visual Studio, nó cung cấp các hỗ trợ cho thiết kế, phát triển, gở lỗi, và triển khai các ứng dụng.
Phát triển, đóng gói, và cung cấp
Thời gian chạy .NET giúp bạn vượt qua phần này một cách rất tốt. Việc phát triển được đơn giản hoá, và trong một số trường hợp sẽ không có một bược cài đặt truyền thống. Bởi vì các gói được triển khai trong một định dạng chung, nên một gói đơn có thể thực thi trong bất kỳ môi trường có hỗ trợ .NET nào. Cuối cùng, môi trường tách biệt các thành phần ứng dụng để mà một ứng dụng chỉ thực thi với các thành phần được gắn với nó, thay vì với các phiên bản khác nhau được gắn bởi các ứng dụng khác.
Dữ liệu mêta (metadata)
Dữ liệu mêta là hồ dán giữ thời gian chạy .NET với nhau. Dữ liệu mêta là tương tự thư viện kiểu trong thế giới COM, nhưng với nhiều thông tin mở rộng hơn.
Đối với mỗi đối tượng là một phần của thế giới .NET, thì dữ liệu mêta cho đối tượng đó ghi tất cả thông tin được đòi hỏi để sử dụng đối tượng, bao gồm:
- Tên đối tượng
- Tên tất cả các trường của đối tượng, và kiểu của chúng
- Tên tất cả các hàm thành phần, kể cả tên và kiểu tham số
Với thông tin này, thời gian chạy .NET có thể hiểu cách tạo các đối tượng, gọi hàm thành phần, hay truy xuất dữ liệu đối tượng, và trình biên dịch có thể sử dụng chúng để tìm ra các đối tượng nào là sẵn có và cách một đối tượng được sử dụng.
Sự thống nhất này là rất tốt cho cả nhà sản xuất lẫn khách hàng của mã; người viết mã có thể dễ dàng viết mã mà có thể được sử dụng từ tất cả các ngôn ngữ thích hợp .NET, và người sử dụng mã có thể dễ dàng sử dụng các đối tượng được tạo từ người khác, bất chấp ngôn ngữ mà các đối tượng được cài đặt.
Ngoài ra, dữ liệu mêta phong phú này cho phép các công cụ khác truy xuất thông tin chi tiết về mã. Visual Studio sử dụng thông tin này trong Object Browser và cho những đặc tính như IntelliSense.
Cuối cùng, mã thời gian chạy có thể truy vấn dữ liệu mêta – bằng một phương pháp gọi là phép phản chiếu – để tìm ra các đối tượng nào là sẵn có và các hàm và trường nào là có mặt trong lớp. Điều này là tương tự như cách xử lý với Idispatch trong thế giới COM, nhưng với một mô hình đơn giản hơn. Tất nhiên, cách truy xuất như thế là không được phân loại một cách mạnh mẽ, nên đa số phần mềm sẽ chọn tham chiếu dữ liệu mêta tại thời gian biên dịch hơn là thời gian chạy, nhưng nó là một phương tiện rất hữu ích cho các ứng dụng như các ngôn ngữ đạo diễn.
Cuối cùng, phép phản chiếu là sẵn có cho người dùng đầu cuối để xác định các đối tượng là như thế nào, để tìm kiếm các đặc tính, hay để thực thi các phương thức mà tên của nó không được biết cho đến thời gian chạy.
Hợp tập
Trong quá khứ, một gói phần mềm hoàn tất có thể được phát hành như một thành phần thực thi được, các tập tin DLL và LIB, DLL chứa đựng một đối tượng COM và một thư viện kiểu, hay một số cơ chế khác.
Trong thời gian chạy .NET, cơ chế của việc đóng gói là hợp tập (assembly). Khi mã được biên dịch bằng một trong những trình biên dịch .NET, nó được chuyển đổi thành một dạng thức trung gian là “IL”. Hợp tập chứa tất cả IL, dữ liệu mêta, và các tập tin khác được đòi hỏi cho một gói để thực thi, trong một gói hoàn chỉnh. Mỗi hợp tập chứa một bản khai (manifest) mà nó liệt kê các tập tin được chứa bên trong hợp tập, kiểm soát các kiểu và tài nguyên được phơi bày ra ngoài hợp tập, và lập các tham chiếu từ các kiểu và tài nguyên đến các tập tin chứa các kiểu và tài nguyên đó. Bản khai cũng liệt kê các hợp tập khác mà hợp tập phụ thuộc vào.
Các hợp tập là tự chứa đựng; có đủ thông tin trong hợp tập cho nó để tự mô tả.
Khi định nghĩa một hợp tập, hợp tập có thể được chứa đựng trong một tập tin đơn lẻ hoặc có thể được chia ra trong vài tập tin. Việc sử dụng vài tập tin sẽ cho phép một viễn cảnh mà ở đó các phần của một hợp tập chỉ được tải khi cần.
Languge Interop
Một trong những mục đích của thời gian chạy .NET là ngôn ngữ-bất khả tri, cho phép mã được sử dụng và viết từ bất kỳ ngôn ngữ nào tiện lợi. Không chỉ có thể các lớp được viết bằng Visual Basic được gọi từ C# hay C++ (hay bất kỳ ngôn ngữ .NET khác), một lớp mà được viết bằng Visual Basic có thể được sử dụng như một lớp cơ sở cho một lớp được viết bằng C#, và lớp đó có thể được sử dụng trong một lớp C++.
Người lại, không quan trọng ngôn ngữ nào được dùng để viết một lớp. Hơn nữa, thường không thể nói một lớp được viết bằng ngôn ngữ nào.
Trong thực tế, mục tiêu này gặp một chút chướng ngại. Một số ngôn ngữ có các kiểu không dấu mà nó không được hỗ trợ bởi các ngôn ngữ khác, và một số ngôn ngữ hỗ trợ chồng hàm. Việc cho phép các ngôn ngữ nhiều đặc tính hơn để giữ lại sự tự do biểu diễn của chúng trong khi vẫn phải chắc rằng các lớp của chúng có thể interop với các ngôn ngữ khác đang thử thách.
Để hỗ trợ cho điều này, thời gian chạy .NET hỗ trợ đủ để cho phép các ngôn ngữ nhiều đặc tính có thể biểu diễn được thêm vào, nên mã được viết bằng một trong các ngôn ngữ này không bị cưỡng ép bởi các ngôn ngữ đơn giản hơn.
Để các lớp có thể được sử dụng từ toàn bộ các ngôn ngữ .NET, các lớp phải trung thành với Common Language Specification (CLS), mô tả các đặc tính có thể nhận thấy được trong giao diện chung của lớp (bất kỳ đặc tính nào có thể được sử dụng một cách nội tại trong một lớp). Ví dụ, CLS ngăn chặn phơi bày các kiểu dữ liệu không dấu, bởi vì không phải là tất cả các ngôn ngữ đều có thể sử dụng chúng. Nhiều thông tin trong CLS có thể được tìm thấy trong .NET SDK, trong mục “Thao tác giữa các ngôn ngữ chéo”.
Một người sử dụng viết mã C# có thể báo rằng nó được cho là tương hợp CLS, và trình biên dịch sẽ đánh dấu bất kỳ vùng nào không tương hợp. Để có nhiều thông tin hơn về các giới hạn cụ thể của mã C# bởi sự tương hợp CLS, hãy xem mục “Tương hợp CLS” trong Chương 31,“Sâu hơn với C#”.
Các đặc tính
Để chuyển đối một lớp thành một thành phần, một số thông tin bổ sung thường được đòi hỏi, như cách để tiếp tục tồn tại một lớp tới đĩa hay cách các giao dịch nên được xử lý. Hướng tiếp cận truyền thống là viết thông tin trong một tập tin tách biệt và sau đó kết hợp nó với mã nguồn để tạo một thành phần.
Vấn đề với hướng tiếp cận này là thông tin này được sao lại trong nhiều nơi. Nó là cồng kềnh và dễ gặp lỗi, và điều đó có nghĩa là bạn không có toàn bộ thành phần trừ khi bạn có tất cả các tập tin.
Thời gian chạy .NET hỗ trợ các đặc tính tuỳ chọn (được biết đơn giản như các đặc tính(attribute) trong C#), mà nó là một cách để đặt các thông tin mô tả trong dữ liệu mêta cùng với một đối tượng, và sau đó truy lục dữ liệu sau này. Các đặc tính cung cấp một cơ chế chung để thực hiện điều này, và chúng được sử dụng nặng nề khắp cả thời gian chạy để lưu trữ thông tin mà sửa đổi cách thời gian chạy sử dụng lớp.
Các đặc tính là hoàn toàn dễ mở rộng, và nó cho phép người lập trình định nghĩa các đặc tính và sử dụng chúng.