Bạn cần một cơchế đơn giản đểtruyền dữliệu trạng thái hay cấu hình giữa các
miền ứng dụng.
Dùng các phương thức SetDatavà GetDatacủa lớp AppDomain.
Dữliệu có thể được truyền qua các miền ứng dụng như đối sốhay trịtrảvềkhi bạn cho gọi
các thành viên của các đối tượng hiện có trong các miền ứng dụng. Việc truyền dữliệu qua
các miền ứng dụng được thực hiện dễdàng giống nhưtruyền dữliệu trong cùng một miền
ứng dụng.
Mọi miền ứng dụng đều duy trì một vùng đệm dữliệu (data cache) chứa một tập các cặp
“tên/giá trị”. Hầu hết nội dung của vùng đệm dữliệu phản ánh các thiết lập cấu hình của miền
ứng dụng, nhưcác giá trịtừ đối tượng AppDomainSetup được cung cấp trong quá trình tạo
miền ứng dụng (xem mục 3.1). Vùng đệm dữliệu này có thể được sửdụng đểtrao đổi dữliệu
giữa các miền ứng dụng hay lưu trữcác giá trịtạm thời dùng trong cùng một miền ứng dụng.
Phương thức SetDatathực hiện việc kết hợp một khóa dạng chuỗi với một đối tượng và lưu
trữnó vào vùng đệm dữliệu của miền ứng dụng. Phương thức GetDatathực hiện công việc
ngược lại là lấy lại đối tượng từvùng đệm dữliệu thông qua khóa. Nếu mã lệnh trong một
miền ứng dụng gọi phương thức SetDatahay GetData đểtruy xuất vùng đệm dữliệu của miền
ứng dụng khác, thì đối tượng dữliệu phải hỗtrợngữnghĩa marshal-by-valuehay marshal-by-reference, nếu không thì ngoại lệ System.Runtime.Serialization.SerializationException
sẽbịném (xem mục 3.3 đểbiết thêm chi tiết vềcách truyền đối tượng qua các miền ứng
dụng). Đoạn mã sau trình bày cách sửdụng phương thức SetDatavà GetData đểtruyền một
System.Collections.ArrayListgiữa hai miền ứng dụng.
53 trang |
Chia sẻ: maiphuongdc | Lượt xem: 2236 | Lượt tải: 1
Bạn đang xem trước 20 trang tài liệu Giáo trình Các giải pháp lập trình C#, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
atters.Binary;
public class BinarySerializationExample {
public static void Main() {
// Tạo và cấu hình ArrayList để tuần tự hóa.
ArrayList people = new ArrayList();
people.Add("Phuong");
people.Add("Phong");
people.Add("Nam");
// Tuần tự hóa đối tượng ArrayList.
FileStream str = File.Create("people.bin");
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(str, people);
str.Close();
// Giải tuần tự hóa đối tượng ArrayList.
str = File.OpenRead("people.bin");
bf = new BinaryFormatter();
people = (ArrayList)bf.Deserialize(str);
str.Close();
// Hiển thị nội dung của đối tượng ArrayList
// đã-được-giải-tuần-tự-hóa.
foreach (string s in people) {
System.Console.WriteLine(s);
}
}
}
Bạn có thể sử dụng lớp SoapFormatter theo cách như được trình bày trong lớp
BinarySerializationExample ở trên, chỉ cần thay mỗi thể hiện của lớp BinaryFormatter bằng
thể hiện của lớp SoapFormatter và thay đổi chỉ thị using để nhập không gian tên
System.Runtime.Serialization.Formatters.Soap. Ngoài ra, bạn cần thêm một tham chiếu
đến System.Runtime.Serialization.Formatters.Soap.dll khi biên dịch mã. File
SoapSerializationExample.cs trong đĩa CD đính kèm sẽ trình bày cách sử dụng lớp
SoapFormatter.
Hình 2.1 và 2.2 dưới đây minh họa hai kết quả khác nhau khi sử dụng lớp BinaryFormatter
và SoapFormatter. Hình 2.1 trình bày nội dung của file people.bin được tạo ra khi sử dụng
BinaryFormatter, hình 2.2 trình bày nội dung của file people.xml được tạo ra khi sử dụng
SoapFormatter.
72
Chương 2: Thao tác dữ liệu
Hình 2.1 Nội dung file people.bin
Hình 2.2 Nội dung file people.xml
73
Chương 2: Thao tác dữ liệu
74
3
Chương 3: MIỀN ỨNG DỤNG, CƠ CHẾ PHẢN CHIẾU, VÀ
SIÊU DỮ LIỆU
75
76
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
ức mạnh và tính linh hoạt của Microsoft .NET Framework được nâng cao bởi khả năng
kiểm tra và thao tác các kiểu và siêu dữ liệu lúc thực thi. Các mục trong chương này sẽ
trình bày các khía cạnh thông dụng của miền ứng dụng (application domain), cơ chế
phản chiếu (reflection), và siêu dữ liệu (metadata), bao gồm:
Tạo và hủy các miền ứng dụng (mục 3.1 và 3.9).
Làm việc với các kiểu và các đối tượng khi sử dụng nhiều miền ứng dụng (mục 3.2,
3.3, 3.4, và 3.8).
Làm việc với thông tin Type (mục 3.10 và 3.11).
Tạo động các đối tượng và nạp động các assembly lúc thực thi (mục 3.5, 3.6, 3.7, và
3.12).
Tạo và kiểm tra các đặc tính tùy biến (các mục 3.13 và 3.14).
3.1 Tạo miền ứng dụng
Bạn cần tạo một miền ứng dụng mới.
Sử dụng phương thức tĩnh CreateDomain của lớp System.AppDomain.
Dạng thức đơn giản nhất của phương thức CreateDomain nhận một đối số kiểu string chỉ
định tên thân thiện cho miền ứng dụng mới. Các dạng thức khác cho phép bạn chỉ định chứng
cứ (evidence) và các thiết lập cấu hình cho miền ứng dụng mới. Chứng cứ được chỉ định bằng
đối tượng System.Security.Policy.Evidence; mục 13.11 trình bày các tác động của chứng
cứ khi bạn tạo một miền ứng dụng. Các thiết lập cấu hình được chỉ định bằng đối tượng
System.AppDomainSetup.
Lớp AppDomainSetup chứa các thông tin cấu hình cho một miền ứng dụng. Bảng 3.1 kiệt kê
các thuộc tính thường được sử dụng nhất của lớp AppDomainSetup khi tạo các miền ứng dụng.
Các thuộc tính này có thể được truy xuất sau khi tạo thông qua các thành viên của đối tượng
AppDomain, và một số có thể thay đổi lúc thực thi; bạn hãy tham khảo tài liệu .NET
Framework SDK về lớp AppDomain để hiểu chi tiết hơn.
Bảng 3.1 Các thuộc tính thông dụng của lớp AppDomainSetup
Thuộc tính Mô tả
ApplicationBase
Thư mục mà CRL sẽ xét trong quá trình dò tìm các assembly
riêng. Kỹ thuật dò tìm (probing) sẽ được thảo luận trong mục
3.5. Thực tế, ApplicationBase là thư mục gốc cho ứng dụng
đang thực thi. Theo mặc định, đây là thư mục chứa assembly.
Có thể đọc được thuộc tính này sau khi tạo miền ứng dụng
bằng thuộc tính AppDomain.BaseDirectory.
ConfigurationFile
Tên của file cấu hình, được sử dụng bởi mã đã được nạp vào
miền ứng dụng. Có thể đọc được thuộc tính này sau khi tạo
miền ứng dụng bằng phương thức AppDomain.GetData với
khóa APP_CONFIG_FILE.
S
77
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
DisallowPublisherPolicy
Quy định phần publisher policy của file cấu hình ứng dụng
có được xét đến hay không khi xác định phiên bản của một
assembly tên mạnh để nối kết. Publisher policy sẽ được thảo
luận trong mục 3.5.
PrivateBinPath
Danh sách các thư mục cách nhau bởi dấu chấm phẩy mà bộ
thực thi sẽ sử dụng khi dò tìm các assembly riêng. Các thư
mục này có vị trí tương đối so với thư mục được chỉ định
trong ApplicationBase. Có thể đọc được thuộc tính này sau
khi tạo miền ứng dụng bằng thuộc tính
AppDomain.RelativeSearchPath. Có thể thay đổi thuộc tính
này lúc thực thi bằng phương thức AppendPrivatePath và
ClearPrivatePath.
Ví dụ dưới đây trình bày cách tạo và cấu hình một miền ứng dụng:
// Khởi tạo một đối tượng của lớp AppDomainSetup.
AppDomainSetup setupInfo = new AppDomainSetup();
// Cấu hình các thông tin cài đặt cho miền ứng dụng.
setupInfo.ApplicationBase = @"C:\MyRootDirectory";
setupInfo.ConfigurationFile = "MyApp.config";
setupInfo.PrivateBinPath = "bin;plugins;external";
// Tạo một miền ứng dụng mới (truyền null làm đối số chứng cứ).
// Nhớ lưu một tham chiếu đến AppDomain mới vì nó
// không thể được thu lấy theo bất kỳ cách nào khác.
AppDomain newDomain = AppDomain.CreateDomain(
"My New AppDomain",
new System.Security.Policy.Evidence(),
setupInfo);
Bạn phải duy trì một tham chiếu đến đối tượng AppDomain vừa tạo bởi vì không có
cơ chế nào để liệt kê các miền ứng dụng hiện có từ bên trong mã được-quản-lý.
3.2 Chuyển các đối tượng qua lại các miền ứng dụng
Bạn cần chuyển các đối tượng qua lại giữa các miền ứng dụng như các đối số hay
các giá trị trả về.
Sử dụng các đối tượng marshal-by-value hay marshal-by-reference.
Hệ thống .NET Remoting (sẽ được thảo luận trong chương 12) giúp việc gởi các đối tượng
qua lại các miền ứng dụng trở nên dễ dàng. Tuy nhiên, nếu bạn chưa quen với .NET
Remoting, kết quả có thể rất khác so với mong đợi. Thực ra, vấn đề gây khó khăn khi dùng
nhiều miền ứng dụng là sự tương tác với .NET Remoting và cách thức luân chuyển đối tượng
qua các miền ứng dụng.
Tất cả các kiểu dữ liệu có thể chia thành ba loại: nonremotable, marshal-by-value (MBV), và
marshal-by-reference (MBR). Kiểu nonremotable không thể vượt qua biên miền ứng dụng và
78
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
không thể dùng làm các đối số hay các giá trị trả về của các lời gọi trong môi trường liên miền
ứng dụng. Kiểu nonremotable sẽ được thảo luận trong mục 3.4.
Kiểu MBV là kiểu khả-tuần-tự-hóa. Khi một đối tượng kiểu MBV được chuyển qua một miền
ứng dụng khác như là đối số hay giá trị trả về, hệ thống .NET Remoting sẽ tuần tự hóa trạng
thái hiện tại của đối tượng, chuyển dữ liệu đó sang miền ứng dụng đích, và tạo một bản sao
của đối tượng với cùng trạng thái như đối tượng gốc. Kết quả là tồn tại bản sao của đối tượng
ở cả hai miền ứng dụng. Hai đối tượng này ban đầu giống nhau hoàn toàn, nhưng độc lập
nhau, nên việc thay đổi đối tượng này không ảnh hưởng đến đối tượng kia. Dưới đây là ví dụ
một kiểu khả-tuần-tự-hóa có tên là Employee, được chuyển qua một miền ứng dụng khác bằng
trị (xem mục 16.1 để biết cách tạo kiểu khả-tuần-tự-hóa).
[System.Serializable]
public class Employee {
// Hiện thực các thành viên ở đây.
§
}
Kiểu MBR là lớp dẫn xuất từ lớp System.MarshalByRefObject. Khi một đối tượng kiểu MBR
được chuyển qua một miền ứng dụng khác như đối số hay giá trị trả về, hệ thống .NET
Remoting sẽ tạo một đối tượng proxy cho đối tượng MBV cần chuyển trong miền ứng dụng
đích. Đối tượng đại diện thực hiện các hành vi hoàn toàn giống với đối tượng MBR mà nó đại
diện. Thực ra, khi thực hiện một hành vi trên đối tượng đại diện, hệ thống .NET Remoting
thực hiện ngầm việc chuyển lời gọi và các đối số cần thiết đến miền ứng dụng nguồn, và tại
đó thực hiện lời gọi hàm trên đối tượng MBR gốc. Kết quả được trả về thông qua đối tượng
đại diện. Dưới đây là một phiên bản khác của lớp Employee, được chuyển qua một miền ứng
dụng khác bằng tham chiếu thay vì bằng trị (xem mục 12.7 để biết chi tiết về cách tạo kiểu
MBR).
public class Employee : System.MarshalByRefObject {
// Hiện thực các thành viên ở đây.
§
}
3.3 Tránh nạp các assembly không cần thiết vào miền
ứng dụng
Bạn cần chuyển một tham chiếu đối tượng qua lại giữa các miền ứng dụng khác
nhau; tuy nhiên, bạn không muốn CLR nạp siêu dữ liệu mô tả kiểu của đối tượng
vào các miền ứng dụng trung gian.
Đóng gói tham chiếu đối tượng trong một System.Runtime.Remoting.ObjectHandle
và khi cần truy xuất đối tượng thì khôi phục lại.
Khi bạn truyền một đối tượng marshal-by-value (MBV) qua các miền ứng dụng, bộ thực thi sẽ
tạo một thể hiện mới của đối tượng này trong miền ứng dụng đích. Điều này có nghĩa là bộ
thực thi phải nạp assembly chứa siêu dữ liệu mô tả kiểu của đối tượng vào các miền ứng
dụng. Do đó, việc truyền các tham chiếu MBV qua các miền ứng dụng trung gian sẽ dẫn đến
79
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
việc bộ thực thi nạp các assembly không cần thiết vào các miền ứng dụng này. Một khi đã
được nạp thì các assembly thừa này sẽ không được giải phóng khỏi miền ứng dụng nếu không
giải phóng cả miền ứng dụng chứa chúng (xem mục 3.9).
Lớp ObjectHandle cho phép bạn đóng gói tham chiếu đối tượng để truyền qua các miền ứng
dụng mà bộ thực thi không phải nạp thêm assembly. Khi đối tượng này đến miền ứng dụng
đích, bạn có thể khôi phục tham chiếu đối tượng, bộ thực thi sẽ nạp các assembly cần thiết và
cho phép bạn truy xuất đến đối tượng như bình thường. Để đóng gói một đối tượng (ví dụ
System.Data.DataSet), bạn có thể thực hiện như sau:
// Tạo một DataSet mới.
System.Data.DataSet data1 = new System.Data.DataSet();
// Cấu hình/thêm dữ liệu cho DataSet.
§
// Đóng gói DataSet.
System.Runtime.Remoting.ObjectHandle objHandle =
new System.Runtime.Remoting.ObjectHandle(data1);
Để khôi phục một đối tượng, sử dụng phương thức ObjectHandle.Unwrap và ép kiểu trả về
cho phù hợp, ví dụ:
// Khôi phục DataSet từ ObjectHandle.
System.Data.DataSet data2 =
(System.Data.DataSet)objHandle.Unwrap();
3.4 Tạo kiểu không thể vượt qua biên miền ứng dụng
Bạn cần tạo một kiểu dữ liệu sao cho các thể hiện của kiểu này không thể được
truy xuất từ mã lệnh ở các miền ứng dụng khác.
Phải chắc chắn kiểu dữ liệu thuộc dạng nonremotable, tức là không thể tuần tự
hóa cũng như không dẫn xuất từ lớp MarshalByRefObject.
Đôi khi bạn muốn kiểu dữ liệu nào đó chỉ được giới hạn truy xuất trong phạm vi của miền
ứng dụng. Để tạo kiểu dữ liệu dạng nonremotable, phải chắc rằng kiểu này không phải là khả-
tuần-tự-hóa và cũng không dẫn xuất (trực tiếp hay gián tiếp) từ lớp MarshalByRefObject.
Những điều kiện này sẽ đảm bảo rằng trạng thái của đối tượng không thể được truy xuất từ
các miền ứng dụng khác (các đối tượng này không thể được sử dụng làm đối số hay giá trị trả
về trong các lời gọi phương thức liên miền ứng dụng).
Điều kiện kiểu dữ liệu không phải là khả-tuần-tự-hóa được thực hiện dễ dàng do một lớp
không thừa kế khả năng tuần tự hóa từ lớp cha của nó. Để bảo đảm một kiểu không phải là
khả-tuần-tự-hóa, bạn phải chắc chắn rằng đặc tính System.SerializableAttribute không
được áp dụng khi khai báo kiểu.
Bạn cần lưu ý khi đảm bảo một lớp không được truyền bằng tham chiếu. Nhiều lớp trong thư
viện lớp .NET dẫn xuất trực tiếp hay gián tiếp từ MarshalByRefObject; bạn phải cẩn thận
không dẫn xuất lớp của bạn từ các lớp này. Những lớp cơ sở thông dụng dẫn xuất từ
MarshalByRefObject bao gồm: System.ComponentModel.Component, System.IO.Stream,
System.IO.TextReader, System.IO.TextWriter, System.NET.WebRequest, và System.Net.
80
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
WebResponse (xem tài liệu .NET Framework SDK để có danh sách đầy đủ các lớp dẫn xuất từ
MarshalByRefObject).
3.5 Nạp assembly vào miền ứng dụng hiện hành
Bạn cần nạp một assembly vào miền ứng dụng lúc thực thi.
Sử dụng phương thức tĩnh Load hay LoadFrom của lớp System.Reflection.Assembly.
Bộ thực thi tự động nạp các assembly mà assembly của bạn tham chiếu đến lúc biên dịch. Tuy
nhiên, bạn cũng có thể chỉ thị cho bộ thực thi nạp assembly. Các phương thức Load và
LoadFrom đều thực hiện một công việc là nạp một assembly vào miền ứng dụng hiện hành, và
cả hai đều trả về một đối tượng Assembly mô tả assembly vừa được nạp. Sự khác biệt giữa hai
phương thức là danh sách các đối số được cung cấp để nhận dạng assembly cần nạp, và cách
thức bộ thực thi định vị assembly này.
Phương thức Load cung cấp nhiều dạng thức cho phép chỉ định assembly cần nạp, bạn có thể
sử dụng một trong những dạng sau:
• Một string chứa tên đầy đủ hay tên riêng phần để nhận dạng assembly.
• Một System.Reflection.AssemblyName mô tả chi tiết về assembly.
• Một mảng byte chứa dữ liệu cấu thành assembly.
Thông thường, tên của assembly được sử dụng để nạp assembly. Tên đầy đủ của một
assembly bao gồm: tên, phiên bản, bản địa, và token khóa công khai, được phân cách bởi dấu
phẩy (ví dụ: System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=
b77a5c561934e089). Để chỉ định một assembly không có tên mạnh, sử dụng
PublicKeyToken=null. Bạn cũng có thể sử dụng tên ngắn để nhận dạng một assembly nhưng ít
nhất phải cung cấp tên của assembly (không có phần mở rộng). Đoạn mã dưới đây trình bày
các cách sử dụng phương thức Load:
// Nạp assembly System.Data dùng tên đầy đủ.
string name1 = "System.Data,Version=1.0.5000.0," +
"Culture=neutral,PublicKeyToken=b77a5c561934e089";
Assembly a1 = Assembly.Load(name1);
// Nạp assembly System.Xml dùng AssemblyName.
AssemblyName name2 = new AssemblyName();
name2.Name = "System.Xml";
name2.Version = new Version(1,0,5000,0);
name2.CultureInfo = new CultureInfo("");
name2.SetPublicKeyToken(
new byte[] {0xb7,0x7a,0x5c,0x56,0x19,0x34,0xe0,0x89});
Assembly a2 = Assembly.Load(name2);
// Nạp assembly SomeAssembly dùng tên ngắn.
Assembly a3 = Assembly.Load("SomeAssembly");
Khi phương thức Load được gọi, bộ thực thi thực hiện quá trình định vị và nạp assembly.
Dưới đây sẽ tóm tắt quá trình này; bạn tham khảo tài liệu .NET Framework SDK để biết thêm
chi tiết.
81
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
1. Nếu bạn chỉ định assembly tên mạnh, phương thức Load sẽ áp dụng version policy
(chính sách phiên bản) và publisher policy (chính sách nhà phát hành) để cho phép khả
năng “chuyển tiếp” (redirect) đến một phiên bản assembly khác. Version policy được
chỉ định trong file cấu hình máy tính hay ứng dụng của bạn bằng phần tử
. Publisher policy được chỉ định trong các assembly đặc biệt được
cài đặt bên trong GAC (Global Assembly Cache).
2. Một khi đã xác định đúng phiên bản của assembly cần sử dụng, bộ thực thi sẽ cố gắng
nạp các assembly tên mạnh từ GAC.
3. Nếu assembly không có tên mạnh hoặc không được tìm thấy trong GAC, bộ thực thi sẽ
tìm phần tử trong các file cấu hình máy tính và ứng dụng. Phần tử
ánh xạ tên của assembly thành một file hay một URL. Nếu assembly có tên
mạnh, có thể chỉ đến bất kỳ vị trí nào kể cả các URL dựa-trên-Internet; nếu
không, phải chỉ đến một thư mục có vị trí tương đối so với thư mục ứng
dụng. Nếu assembly không tồn tại trong vị trí được chỉ định, phương thức Load sẽ ném
ngoại lệ System.IO.FileNotFoundException.
4. Nếu không có phần tử tương ứng với assembly, bộ thực thi sẽ tìm assembly
bằng kỹ thuật probing. Quá trình probing sẽ tìm file đầu tiên có tên của assembly (với
phần mở rộng là .dll hay .exe) trong các vị trí:
• Thư mục gốc của ứng dụng.
• Các thư mục con của thư mục gốc phù hợp với tên và bản địa của assembly.
• Các thư mục con (của thư mục gốc) do người dùng chỉ định.
Phương thức Load là cách dễ nhất để tìm và nạp các assembly, nhưng cũng có thể tốn nhiều
chi phí cho việc dò trong nhiều thư mục để tìm các assembly có tên yếu. Phương thức
LoadFrom cho phép bạn nạp assembly từ một vị trí xác định, nếu không tìm thấy nó sẽ ném
ngoại lệ FileNotFoundException. Bộ thực thi sẽ không cố tìm assembly như phương thức
Load—phương thức LoadFrom không hỗ trợ GAC, policy, phần tử hay probing.
Dưới đây là đoạn mã trình bày cách sử dụng LoadFrom để nạp
c:\shared\MySharedAssembly.dll. Lưu ý rằng, khác với Load, LoadFrom yêu cầu bạn chỉ định
phần mở rộng của file assembly.
// Nạp assembly có tên là c:\shared\MySharedAssembly.dll
Assembly a4 = Assembly.LoadFrom(@"c:\shared\MySharedAssembly.dll");
3.6 Thực thi assembly ở miền ứng dụng khác
Bạn cần thực thi một assembly ở một miền ứng dụng khác với miền ứng dụng hiện
hành.
Gọi phương thức ExecuteAssembly của đối tượng AppDomain đại diện cho miền ứng
dụng, và chỉ định tên của assembly cần thực thi.
Nếu bạn có một assembly khả-thực-thi và muốn nạp để thực thi nó trong một miền ứng dụng,
phương thức ExecuteAssembly sẽ giúp bạn. Phương thức ExecuteAssembly có bốn dạng thức
khác nhau. Dạng thức đơn giản nhất chỉ nhận vào một kiểu string chứa tên của assembly cần
thực thi; bạn có thể chỉ định một file cục bộ hay một URL. Một dạng thức khác cho phép bạn
82
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
chỉ định chứng cứ (evidence) cho assembly (xem mục 13.10) và các đối số để truyền đến điểm
nhập của assembly (tương đương với các đối số dòng lệnh).
Phương thức ExecuteAssembly nạp assembly được chỉ định và thực thi phương thức được
định nghĩa trong siêu dữ liệu là điểm nhập của assembly (thường là phương thức Main). Nếu
assembly được chỉ định không có khả năng thực thi, ExecuteAssembly sẽ ném ngoại lệ
System.Runtime.InteropServices.COMException. Bộ thực thi không thực thi assembly trong
một tiểu trình mới, vì thế quyền kiểm soát sẽ không trả về cho đến khi quá trình thực thi của
assembly kết thúc. Do ExecuteAssembly nạp một assembly bằng tên riêng phần (chỉ có tên
file), CLR sẽ không dùng GAC hay probing để tìm assembly (xem mục 3.5 để biết thêm chi
tiết).
Ví dụ dưới đây trình bày cách sử dụng phương thức ExecuteAssembly để nạp và thực thi một
assembly. Lớp ExecuteAssemblyExample tạo một AppDomain và thực thi chính nó trong
AppDomain bằng phương thức ExecuteAssembly. Kết quả là có hai bản sao của
ExecuteAssemblyExample được nạp vào hai miền ứng dụng khác nhau.
using System;
public class ExecuteAssemblyExample {
public static void Main(string[] args) {
// Nếu assembly đang thực thi trong một AppDomain
// có tên thân thiện là "NewAppDomain"
// thì không tạo AppDomain mới. Điều này sẽ
// tránh một vòng lặp vô tận tạo AppDomain.
if (AppDomain.CurrentDomain.FriendlyName != "NewAppDomain") {
// Tạo miền ứng dụng mới có tên là "NewAppDomain".
AppDomain domain = AppDomain.CreateDomain("NewAppDomain");
// Thực thi assembly này trong AppDomain mới và
// truyền mảng các đối số dòng lệnh.
domain.ExecuteAssembly("ExecuteAssemblyExample.exe",
null, args);
}
// Hiển thị các đối số dòng lệnh lên màn hình
// cùng với tên thân thiện của AppDomain.
foreach (string s in args) {
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName +
" : " + s);
}
}
}
3.7 Thể hiện hóa một kiểu trong miền ứng dụng khác
Bạn cần thể hiện hóa một kiểu trong một miền ứng dụng khác với miền ứng dụng
hiện hành.
Gọi phương thức CreateInstance hay CreateInstanceFrom của đối tượng AppDomain
đại diện cho miền ứng dụng đích.
83
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
Việc sử dụng phương thức ExecuteAssembly (đã được thảo luận trong mục 3.6) không mấy
khó khăn; nhưng khi phát triển các ứng dụng phức tạp có sử dụng nhiều miền ứng dụng, chắc
chắn bạn muốn kiểm soát quá trình nạp các assembly, tạo các kiểu dữ liệu, và triệu gọi các
thành viên của đối tượng bên trong miền ứng dụng.
Các phương thức CreateInstance và CreateInstanceFrom cung cấp nhiều phiên bản nạp
chồng giúp bạn kiểm soát quá trình tạo đối tượng. Các phiên bản đơn giản nhất sử dụng
phương thức khởi dựng mặc định của kiểu, nhưng cả hai phương thức này đều thiết đặt các
phiên bản cho phép bạn cung cấp đối số để sử dụng bất kỳ phương thức khởi dựng nào.
Phương thức CreateInstance nạp một assembly có tên xác định vào miền ứng dụng bằng quá
trình đã được mô tả cho phương thức Assembly.Load trong mục 3.5. Sau đó, CreateInstance
tạo đối tượng cho kiểu và trả về một tham chiếu đến đối tượng mới được đóng gói trong
ObjectHandle (được mô tả trong mục 3.3). Tương tự như thế đối với phương thức
CreateInstanceFrom; tuy nhiên, CreateInstanceFrom nạp assembly vào miền ứng dụng bằng
quá trình đã được mô tả cho phương thức Assembly.LoadFrom trong mục 3.5.
AppDomain cũng cung cấp hai phương thức rất tiện lợi có tên là
CreateInstanceAndUnwrap và CreateInstanceFromAndUnwrap, chúng sẽ tự động khôi
phục tham chiếu đến đối tượng đã được tạo từ đối tượng ObjectHandle; bạn phải
ép đối tượng trả về cho đúng kiểu trước khi sử dụng.
Nếu bạn sử dụng CreateInstance hay CreateInstanceFrom để tạo đối tượng kiểu MBV trong
một miền ứng dụng khác, đối tượng sẽ được tạo nhưng tham chiếu trả về sẽ không chỉ đến đối
tượng đó. Do cách thức đối tượng MBV vượt qua biên miền ứng dụng, tham chiếu này sẽ chỉ
đến một bản sao của đối tượng được tạo tự động trong miền ứng dụng cục bộ. Chỉ khi bạn tạo
một kiểu MBR thì tham chiếu trả về mới chỉ đến đối tượng trong miền ứng dụng khác (xem
mục 3.2 để biết thêm chi tiết về kiểu MBV và MBR).
Kỹ thuật chung để đơn giản hóa việc quản lý các miền ứng dụng là sử dụng lớp điều khiển
(controller class). Một lớp điều khiển là một kiểu MBR tùy biến. Bạn hãy tạo một miền ứng
dụng rồi tạo đối tượng lớp điều khiển trong miền ứng dụng này bằng phương thức
CreateInstance. Lớp điều khiển hiện thực các chức năng cần thiết cho ứng dụng để thao tác
miền ứng dụng và các nội dung của nó. Các chức năng này có thể bao gồm: nạp assembly, tạo
thêm miền ứng dụng, dọn dẹp trước khi xóa miền ứng dụng, hay liệt kê các phần tử chương
trình (bạn không thể thực hiện ở bên ngoài miền ứng dụng).
Ví dụ dưới đây trình bày cách sử dụng một lớp điều khiển có tên là PluginManager. Khi đã
được tạo trong một miền ứng dụng, PluginManager cho phép bạn tạo đối tượng của các lớp có
hiện thực giao diện IPlugin, chạy và dừng các plug-in đó, và trả về danh sách các plug-in
hiện được nạp.
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Specialized;
// Giao diện chung cho tất cả các plug-in.
public interface IPlugin {
void Start();
void Stop();
}
84
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
// Một hiện thực đơn giản cho giao diện Iplugin
// để minh họa lớp điều khiển PluginManager.
public class SimplePlugin : IPlugin {
public void Start() {
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName +
": SimplePlugin starting...");
}
public void Stop() {
85
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName +
": SimplePlugin stopping...");
}
}
// Lớp điều khiển, quản lý việc nạp và thao tác
// các plug-in trong miền ứng dụng của nó.
public class PluginManager : MarshalByRefObject {
// ListDictionary giữ tham chiếu đến các plug-in.
private ListDictionary plugins = new ListDictionary();
// Phương thức khởi dựng mặc định.
public PluginManager() {}
// Phương thức khởi dựng nhận danh sách các plug-in.
public PluginManager(ListDictionary pluginList) {
// Nạp các plug-in đã được chỉ định.
foreach (string plugin in pluginList.Keys) {
this.LoadPlugin((string)pluginList[plugin], plugin);
}
}
// Nạp assembly và tạo plug-in được chỉ định.
public bool LoadPlugin(string assemblyName, string pluginName) {
try {
// Nạp assembly.
Assembly assembly = Assembly.Load(assemblyName);
// Tạo plug-in mới.
IPlugin plugin =
(IPlugin)assembly.CreateInstance(pluginName, true);
if (plugin != null) {
// Thêm plug-in mới vào ListDictionary.
plugins[pluginName] = plugin;
return true;
} else {
return false;
}
} catch {
return false;
}
}
public void StartPlugin(string plugin) {
// Lấy một plug-in từ ListDictionary và
// gọi phương thức Start.
((IPlugin)plugins[plugin]).Start();
}
86
Chương 3: Miền ứng dụng, cơ chế phản chiếu, và siêu dữ liệu
public void StopPlugin(string plugin) {
// Lấy một plug-in từ ListDictionary và
// gọi phương thức Stop.
((IPlugin)plugins[plugin]).Stop();
}
public ArrayList GetPluginList() {
// Trả về danh sách các