1.1 Giải phóng assembly vàmiềnứngdụng V V Bạn cần giải phóng các assembly hay các miềnứngdụng lúc thực thi. # # Không có cách nào để giải phóng các assembly riêng lẻ. Bạn có thể giải phóng toàn bộ một miềnứngdụng bằng phương thức tĩnh AppDomain.Unload, đồng thời với việc giải phóng miềnứngdụng là tất cả các assembly đã được nạp vào miềnứngdụng đó cũng được giải phóng. Cách duy nhất để giải phóng một assembly là giải phóng cả miềnứngdụng mà nó đã được nạp vào. Đáng ti ếc, việc giải phóng một miềnứngdụng cũng sẽ giải phóng luôn tất cả các assembly đã được nạp vào đó. Đây là một giới hạn yêu cầu bạn phải tổ chức và quản lý tốt cấu trúc miềnứngdụngvà assembly. Khi giải phóng một miềnứngdụng bằng phương thức tĩnh AppDomain.Unload, bạn cần truyền cho nó một tham chiếu AppDomain đến miềnứngdụ ng cần giải phóng. Bạn không thể giải phóng miềnứngdụng mặc định do CLR tạo lúc startup. Đoạn mã dưới đây trình bày cách sử dụng phương thức Unload. // Tạo một miềnứngdụng mới. AppDomain newDomain = AppDomain.CreateDomain("New Domain"); // Nạp assembly vào miềnứngdụng mày. § // Giải phóng miềnứng dụng. AppDomain.Unload(newDomain); Phương thức Unload chặn các tiểu trình mới đi vào miềnứngdụng được chỉ định và gọi phương thức Thread.Abort trên tất cả các tiểu trình hiện đang chạy trong miềnứngdụng này. Nếu tiểu trình gọi phương thức Unload hiện đang chạy trong miềnứngdụng cần giải phóng thì một tiểu trình khác sẽ được khởi chạy để thực hiện quá trình giải phóng. Nếu có v ấn đề trong việc giải phóng miềnứng dụng, ngoại lệ System.CannotUnloadAppDomainException sẽ bị ném bởi tiểu trình thực hiện quá trình giải phóng. Trong khi miềnứngdụng đang được giải phóng, CLR gọi thực thi các phương thức giải phóng của tất cả các đối tượng trong miềnứng dụng. Tùy thuộc vào số lượng đối tượng và bản chất của các phương thức giải phóng mà quá trình này có thể mất một khoảng thời gian nào đó. Phương thức AppDomain.IsFinalizingForUnload trả về true nếu miềnứngdụng đang được giải phóng và CLR đã bắt đầu giải phóng các đối tượng trong đó; ngược lại, trả về false. 1.2 Truy xuất thông tin Type V V Bạn muốn thu lấy đối tượng System.Type mô tả một kiểu dữliệu nhất định. # # Sử dụng một trong các cách sau: • Toán tử typeof • Phương thức tĩnh GetType của lớp System.Type • Phương thức GetType thuộc một thể hiện của kiểu • Phương thức GetNestedType hay GetNestedTypes của lớp Type • Phương thức GetType hay GetTypes của lớp Assembly • Phương thức GetType, GetTypes, hay FindTypes của lớp System.Reflection. Module Đối tượng Type cung cấp một điểm khởi đầu để làm việc với các kiể u dữliệu bằng cơ chếphản chiếu. Một đối tượng Type cho phép bạn kiểm tra siêudữliệu của kiểu, thu lấy các thành viên của kiểu, và tạo các đối tượng của kiểu. Do tầm quan trọng của nó, .NET Framework cung cấp nhiều cơ chế để lấy tham chiếu đến các đối tượng Type. Phương pháp hiệu quả nhất để thu lấy đối tượng Type cho một kiể u cụ thể là sử dụng toán tử typeof: System.Type t1 = typeof(System.Text.StringBuilder); Tên kiểu không được đặt trong dấu nháy kép và phải khả phân giải đối với trình biên dịch. Vì tham chiếu được phân giải lúc biên dịch nên assembly chứa kiểu này trở thành phần phụ thuộc tĩnh của assembly và sẽ được liệt kê như thế trong assembly manifest của bạn. Một cách khác là sử dụng phương thức tĩnh Type.GetType, nhận vào một chuỗi chứa tên kiểu. Vì sử dụng chuỗi để chỉ định kiểu nên bạn có thể thay đổi nó lúc thực thi, điều này mở ra cánh cửa đến với thế giới lập trình động bằng cơ chếphảnchiếu (xem mục 3.12). Nếu bạn chỉ định tên kiểu, bộ thực thi phải tìm kiểu này trong một assembly đã được nạp. Bạn cũng có thể chỉ định một tên kiểu theo tiêu chuẩn assembly (tham khảo tài liệ u .NET Framework SDK về phương thức Type.GetType để biết cách kết cấu tên kiểu theo tiêu chuẩn assembly). Các lệnh sau trình bày cách sử dụng phương thức GetType: // Có phân biệt chữ hoa-thường, trả về null nếu không tìm thấy. Type t2 = Type.GetType("System.String"); // Có phân biệt chữ hoa-thường, // ném ngoại lệ TypeLoadException nếu không tìm thấy. Type t3 = Type.GetType("System.String", true); // Không phân biệt chữ hoa-thường, // ném ngoại lệ TypeLoadException nếu không tìm thấy. Type t4 = Type.GetType("system.string", true, true); // Tên kiểu theo tiêu chuẩn assembly. Type t5 = Type.GetType("System.Data.DataSet,System.Data," + "Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089"); Để thu lấy đối tượng Type mô tả kiểu của một đối tượng hiện có, hãy sử dụng phương thức GetType, được hiện thực bởi Object và được thừa kế bởi tất cả các kiểu dữ liệu. Dưới đây là một ví dụ: System.Text.StringBuilder sb = new System.Text.StringBuilder(); Type t6 = sb.GetType(); Bảng 3.2 tóm tắt các phương thức khác cũng cung cấp khả năng truy xuất đối tượng Type. Bảng 3.2 Các phương thức trả về đối tượng Type Phương thức Mô tả Type.GetNestedType Lấy đối tượng Type mô tả một kiểu lồng bên trong đối tượng Type hiện có Type.GetNestedTypes Lấy một mảng các đối tượng Type mô tả các kiểu lồng bên trong đối tượng Type hiện có Assembly.GetType Lấy đối tượng Type mô tả một kiểu được khai báo bên trong assembly Assembly.GetTypes Lấy một mảng các đối tượng Type mô tả các kiểu được khai báo bên trong assembly Module.GetType Lấy đối tượng Type mô tả một kiểu được khai báo bên trong module Module.GetTypes Lấy một mảng các đối tượng Type mô tả các kiểu được khai báo bên trong module Module.FindTypes Lấy một mảng đã được lọc, chứa các đối tượng Type mô tả các kiểu được khai báo bên trong module—các kiểu này được lọc bằng một delegate (xác định xem mỗi Type có xuất hiện trong mảng đích hay không) 1.3 Kiểm tra kiểu của một đối tượng V V Bạn muốn kiểm tra kiểu của một đối tượng. # # Sử dụng phương thức thừa kế Object.GetType để thu lấy Type cho đối tượng này. Trong vài trường hợp, bạn cũng có thể sử dụng toán tử is và as để kiểm tra kiểu của một đối tượng. Tất cả các kiểu dữliệu đều thừa kế phương thức GetType từ lớp cơ sở Object. Như đã được thảo luận trong mục 3.10, phương thức này trả về một tham chiếu Type mô tả kiểu của đối tượng. Bộ thực thi duy trì một đối tượng Type cho mỗi kiểu được nạp và tất cả các tham chiếu cho kiểu này cùng chỉ đến đối tượng này. Điều này nghĩa là bạn có thể so sánh hai tham chiếu kiểu một cách hiệu quả. Ví dụ dưới đây trình bày cách kiểm tra một đối tượng có phải là System.IO.StringReader hay không: // Tạo một StringReader để thử nghiệm. Object someObject = new StringReader("This is a StringReader"); // Kiểm tra xem someObject có phải là một StringReader hay không // bằng cách thu lấy và so sánh tham chiếu Type (sử dụng toán tử typeof). if (typeof(System.IO.StringReader) == someObject.GetType()) { // Làm gì đó. § } C# cung cấp toán tử is để thực hiện nhanh việc kiểm tra như trên. Ngoài ra, is sẽ trả về true nếu đối tượng cần kiểm tra dẫn xuất từ lớp được chỉ định. Đoạn mã dưới đây kiểm tra xem someObject là một thể hiện của System.IO.TextReader, hay một lớp dẫn xuất từ TextReader (như StringReader): // Kiểm tra xem someObject là TextReader, // hay dẫn xuất từ TextReader bằng toán tử is. if (someObject is System.IO.TextReader) { // Làm gì đó. § } Cả hai cách này đều đòi hỏi kiểu dùng với toán tử typeof và is phải là kiểu đã biết và khả phân giải lúc biên dịch. Một cách khác linh hoạt hơn (nhưng chậm hơn) là sử dụng phương thức Type.GetType để trả về một tham chiếu Type cho kiểu được chỉ định. Tham chiếu Type không được phân giải cho đến khi thực thi, việc này ảnh hưởng đến hiệu năng, nhưng cho phép bạn thay đổi phép so sánh kiểu lúc th ực thi dựa trên giá trị của một chuỗi. Phương thức IsType dưới đây sẽ trả về true nếu đối tượng thuộc kiểu được chỉ định và sử dụng phương thức Type.IsSubclassOf để kiểm tra đối tượng này có phải là một lớp con của kiểu được chỉ định hay không. public static bool IsType(object obj, string type) { Type t = Type.GetType(type, true, true); return t == obj.GetType() || obj.GetType().IsSubclassOf(t); } Cuối cùng, bạn có thể sử dụng toán tử as để ép bất kỳ đối tượng nào sang kiểu được chỉ định. Nếu đối tượng không thể bị ép sang kiểu được chỉ định, toán tử as sẽ trả về null. Điều này cho phép bạn thực hiện các phép ép kiểu an toàn (safe cast), nhưng kiểu được so sánh phải là khả phân giải lúc thực thi. Dưới đây là một ví dụ: // Sử dụng toán tử as để thực hiện một phép ép kiểu an toàn. StringReader reader = someObject as System.IO.StringReader; if (reader != null) { // Làm gì đó. § } Í Phương thức tĩnh GetUnderlyingType của lớp System.Enum cho phép bạn thu lấy kiểu thực sự của một kiểu liệt kê. 1.4 Tạo một đối tượng bằng cơ chếphảnchiếu V V Bạn cần tạo một đối tượng bằng cơ chếphản chiếu. # # Thu lấy đối tượng Type mô tả kiểu của đối tượng cần tạo, gọi phương thức GetConstructor của nó để có được đối tượng System.Reflection.ConstructorInfo mô tả phương thức khởi dựng cần dùng, sau đó thực thi phương thức ConstructorInfo.Invoke. Bước đầu tiên trong việc tạo một đối tượng bằng cơ chếphảnchiếu là thu lấy đối tượng Type mô tả kiểu của đối tượ ng cần tạo (xem mục 3.10 để biết thêm chi tiết). Khi có được đối tượng Type, hãy gọi phương thức GetConstructor để thu lấy đối tượng ConstructorInfo mô tả một trong các phương thức khởi dựng của kiểu này. Dạng thức thông dụng nhất của phương thức GetConstructor là nhận một mảng Type làm đối số, và trả về đối tượng ConstructorInfo mô tả phương thức khởi dựng nhận các đối số đượ c chỉ định trong mảng Type. Để thu lấy đối tượng ConstructorInfo mô tả phương thức khởi dựng mặc định (không có đối số), bạn hãy truyền cho phương thức GetConstructor một mảng Type rỗng (sử dụng trường tĩnh Type.EmptyTypes); đừng sử dụng null, nếu không GetConstructor sẽ ném ngoại lệ System.ArgumentNullException. Nếu GetConstructor không tìm thấy phương thức khởi dựng nào có chữ ký phù hợp với các đối số được chỉ định thì nó s ẽ trả về null. Môt khi đã có đối tượng ConstructorInfo như mong muốn, hãy gọi phương thức Invoke của nó. Bạn phải cung cấp một mảng chứa các đối số mà bạn muốn truyền cho phương thức khởi dựng. Phương thức Invoke sẽ tạo đối tượng mới và trả về một tham chiếu đến đối tượng đó (bạn phải ép về kiểu phù hợp). Dưới đây là đoạn mã trình bày cách tạo một đối tượng System.Text.StringBuilder, chỉ định nội dung ban đầu cho StringBuilder và sức chứa của nó. // Thu lấy đối tượng Type cho lớp StringBuilder. Type type = typeof(System.Text.StringBuilder); // Tạo Type[] chứa các đối tượng Type cho mỗi đối số // của phương thức khởi dựng (một chuỗi và một số nguyên). Type[] argTypes = new Type[] {typeof(System.String), typeof(System.Int32)}; // Thu lấy đối tượng ConstructorInfo. ConstructorInfo cInfo = type.GetConstructor(argTypes); // Tạo object[] chứa các đối số cho phương thức khởi dựng. object[] argVals = new object[] {"Some string", 30}; // Tạo đối tượng và ép nó về kiểu StringBuilder. StringBuilder sb = (StringBuilder)cInfo.Invoke(argVals); Chức năng phảnchiếu thường được sử dụng để hiện thực các factory. Trong đó, bạn sử dụng cơ chếphảnchiếu để thể hiện hóa các lớp thừa kế một lớp cơ sở phổ biến hay hiện thực một giao diện phổ biến. Thông thường, cả lớp cơ sở chung và giao diện chung đều được sử dụng. Lớp c ơ sở trừu tượng sẽ hiện thực giao diện và bất kỳ chức năng chung nào, sau đó mỗi hiện thực cụ thể sẽ thừa kế lớp cơ sở. Không có cơ chế nào để khai báo rằng mỗi lớp cụ thể phải hiện thực các phương thức khởi dựng với các chữ ký cụ thể. Nếu muốn người khác (hãng thứ ba) hiện thực các l ớp cụ thể thì bạn phải chỉ rõ (trong tài liệu hướng dẫn) chữ ký của phương thức khởi dựng mà factory của bạn gọi. Cách thông thường để tránh vấn đề này là sử dụng phương thức khởi dựng mặc định (không có đối số), sau đó cấu hình đối tượng bằng phương thức và thuộc tính. Ví dụ dưới đây sẽ hiện thực một factory dùng để tạo các đối tượng có hiện thực giao diện IPlugin. using System; using System.Reflection; // Giao diện chung mà tất cả các plug-in phải hiện thực. public interface IPlugin { string Description { get; set; } void Start(); void Stop(); } // Lớp cơ sở trừu tượng mà tất cả các plug-in phải dẫn xuất từ đây. public abstract class AbstractPlugin : IPlugin { // Chuỗi chứa lời mô tả plug-in. private string description = ""; // Thuộc tính dùng để lấy lời mô tả plug-in. public string Description { get { return description; } set { description = value; } } // Khai báo các thành viên của giao diện IPlugin. public abstract void Start(); public abstract void Stop(); } // Một hiện thực đơn giản cho giao diện IPlugin // để minh họa lớp PluginFactory. public class SimplePlugin : AbstractPlugin { // Hiện thực phương thức Start. public override void Start() { Console.WriteLine(Description + ": Starting ."); } // Hiện thực phương thức Stop. public override void Stop() { Console.WriteLine(Description + ": Stopping ."); } } // Factory dùng để tạo các đối tượng c ủa IPlugin. public sealed class PluginFactory { public static IPlugin CreatePlugin(string assembly, string pluginName, string description) { // Thu lấy đối tượng Type cho plug-in được chỉ định. Type type = Type.GetType(pluginName + ", " + assembly); // Thu lấy đối tượng ConstructorInfo. ConstructorInfo cInfo = type.GetConstructor(Type.EmptyTypes); // Tạo đối tượng và ép nó về kiểu StringBuilder. IPlugin plugin = (IPlugin)cInfo.Invoke(null); // Cấu hình IPlugin mới. plugin.Description = description; return plugin; } } Câu lệnh sau đây sẽ tạo đối tượng SimplePlugin bằng lớp PluginFactory: IPlugin plugin = PluginFactory.CreatePlugin( "CreateObjectExample", // Tên assembly "SimplePlugin", // Tên lớp plug-in "A Simple Plugin" // Lời mô tả plug-in . một miền ứng dụng bằng phương thức tĩnh AppDomain.Unload, đồng thời với việc giải phóng miền ứng dụng là tất cả các assembly đã được nạp vào miền ứng dụng. ứng dụng mày. § // Giải phóng miền ứng dụng. AppDomain.Unload(newDomain); Phương thức Unload chặn các tiểu trình mới đi vào miền ứng dụng được chỉ định và