1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

Hiện thực kiểu khả-tuần-tự-hóa (serializable type)

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (7.15 MB, 706 trang )


608

Chương 16: Các giao diện và mẫu thông dụng



Đoạn mã dưới đây trình bày các khai báo kiểu và khai báo trường của một lớp khả-tuần-tựhóa có tên là Employee.

using System;



[Serializable]

public class Employee {



private string name;

private int age;

private string address;

§

}







Các lớp dẫn xuất từ một kiểu khả-tuần-tự-hóa không thừa kế đặc tính

SerializableAttribute. Để khiến cho các kiểu dẫn xuất là khả-tuần-tự-hóa, bạn

phải khai báo chúng là khả-tuần-tự-hóa bằng cách áp dụng đặc tính

SerializableAttribute.



Bạn có thể ngăn việc tuần tự hóa một trường nào đó bằng cách áp dụng đặc tính

System.NonSerializedAttribute cho trường này. Bạn nên ngăn việc tuần tự hóa đối với các

trường sau:





Chứa các kiểu dữ liệu không-khả-tuần-tự-hóa.







Chứa các giá trị có thể không hợp lệ khi đối tượng được giải tuần tự hóa, ví dụ: kết nối

cơ sở dữ liệu, địa chỉ bộ nhớ, ID của tiểu trình, và handle của tài nguyên không-đượcquản-lý.







Chứa các thông tin nhạy cảm hay riêng tư, ví dụ: mật khẩu, khóa mật hóa, và các chi

tiết riêng về người hay tổ chức.







Chứa các dữ liệu dễ dàng tái tạo hay thu lấy được từ các nguồn khác—đặc biệt khi dữ

liệu lớn.



Nếu ngăn việc tuần tự hóa một số trường, bạn phải hiện thực kiểu sao cho bù lại việc những

dữ liệu nào đó sẽ không hiện diện khi một đối tượng được giải tuần tự hóa. Đáng tiếc, bạn

không thể tạo hay thu lấy các trường dữ liệu bị mất trong một phương thức khởi dựng vì

formatter không gọi phương thức khởi dựng trong quá trình giải tuần tự hóa đối tượng. Giải

pháp thông thường nhất là hiện thực mẫu “Lazy Initialization”, trong đó kiểu của bạn sẽ tạo

hay thu lấy dữ liệu ngay lần đầu tiên cần đến.

Đoạn mã dưới đây trình bày một phiên bản đã được chỉnh sửa của lớp Employee với đặc tính

NonSerializedAttribute được áp dụng cho trường address, nghĩa là formatter sẽ không tuần

tự hóa giá trị của trường này. Lớp Employee hiện thực các thuộc tính công khai dùng để truy

xuất các thành viên dữ liệu riêng, là nơi thuận tiện để hiện thực “Lazy Initialization” cho

trường address.



609

Chương 16: Các giao diện và mẫu thông dụng

using System;



[Serializable]

public class Employee {



private string name;

private int age;



[NonSerialized]

private string address;



// Phương thức khởi dựng đơn giản.

public Employee(string name, int age, string address) {



this.name = name;

this.age = age;

this.address = address;

}



// Thuộc tính công khai dùng để truy xuất tên của nhân viên.

public string Name {

get { return name; }

set { name = value; }

}



// Thuộc tính công khai dùng để truy xuất tuổi của nhân viên.

public int Age {

get { return age; }

set { age = value; }

}



// Thuộc tính công khai dùng để truy xuất địa chỉ của nhân viên.

// Sử dụng "Lazy Initialization" để thiết lập địa chỉ vì

// đối tượng được-giải-tuần-tự-hóa sẽ không có giá trị địa chỉ.

public string Address {

get {

if (address == null) {



610

Chương 16: Các giao diện và mẫu thông dụng



// Nạp địa chỉ từ kho lưu trữ.

}

return address;

}



set {

address = value;

}

}

}



Đối với phần lớn các kiểu tùy biến, việc sử dụng đặc tính SerializableAttribute và

NonSerializedAttribute sẽ đáp ứng đủ nhu cầu tuần tự hóa của bạn. Nếu cần kiểm soát quá

trình tuần tự hóa, bạn cần hiện thực giao diện ISerializable. Các lớp formatter sử dụng logic

khác nhau khi tuần tự hóa và giải tuần tự hóa thể hiện của các kiểu có hiện thực

ISerializable. Để hiện thực đúng ISerializable, bạn phải:





Khai báo rằng kiểu của bạn có hiện thực giao diện ISerializable.







Áp dụng đặc tính SerializableAttribute vào khai báo kiểu như vừa được mô tả;

không sử dụng NonSerializedAttribute vì nó sẽ không có tác dụng.







Hiện thực phương thức ISerializable.GetObjectData (được sử dụng trong quá trình

tuần tự hóa), phương thức này nhận các kiểu đối số sau:





System.Runtime.Serialization.SerializationInfo







System.Runtime.Serialization.StreamingContext







Hiện thực một phương thức khởi dựng không công khai (được sử dụng trong quá trình

giải tuần tự hóa), phương thức này nhận cùng đối số như phương thức GetObjectData.

Nhớ rằng, nếu bạn có ý định dẫn xuất một số lớp từ lớp khả-tuần-tự-hóa thì phương

thức khởi dựng này phải là protected.







Nếu bạn tạo một lớp khả-tuần-tự-hóa từ một lớp cơ sở cũng có hiện thực

ISerializable, thì phương thức GetObjectData và phương thức khởi dựng (dùng để

giải tuần tự hóa) của bạn phải gọi các phương thức tương đương trong lớp cha.



Trong quá trình tuần tự hóa, formatter sẽ gọi phương thức GetObjectData và truyền cho nó

các tham chiếu SerializationInfo và StreamingContext.





Bạn phải đổ dữ liệu cần tuần tự hóa vào đối tượng SerializationInfo. Lớp

SerializationInfo cung cấp phương thức AddValue dùng để thêm dữ liệu. Với mỗi lần

gọi AddValue, bạn phải chỉ định tên dữ liệu (tên này sẽ được sử dụng trong quá trình

giải tuần tự hóa để thu lấy dữ liệu). Phương thức AddValue có đến 16 phiên bản nạp

chồng, cho phép bạn thêm nhiều kiểu dữ liệu khác nhau vào đối tượng

SerializationInfo.



611

Chương 16: Các giao diện và mẫu thông dụng







Đối tượng StreamingContext cung cấp các thông tin về chủ định và đích của dữ liệu

được-tuần-tự-hóa, cho phép bạn chọn tuần tự hóa dữ liệu nào. Ví dụ, bạn có thể cần

tuần tự hóa dữ liệu riêng nếu nó được dành cho một miền ứng dụng khác trong cùng

tiến trình, nhưng không cần nếu dữ liệu sẽ được ghi ra file.



Trong quá trình giải tuần tự hóa, formatter sẽ gọi phương thức khởi dựng việc giải tuần tự

hóa, lại truyền cho nó các tham chiếu SerializationInfo và StreamingContext.





Kiểu của bạn phải trích dữ liệu đã-được-tuần-tự-hóa từ đối tượng SerializationInfo

bằng một trong các phương thức SerializationInfo.Get*, ví dụ: GetString, GetInt32,

hay GetBoolean.







Đối tượng StreamingContext cung cấp các thông tin về nguồn gốc của dữ liệu đã-đượctuần-tự-hóa, phản ánh logic mà bạn đã hiện thực cho việc tuần tự hóa.







Trong quá trình tuần tự hóa chuẩn, formatter không sử dụng khả năng của đối

tượng StreamingContext để cho biết các chi tiết về nguồn gốc, đích, và chủ định

của dữ liệu được-tuần-tự-hóa. Tuy nhiên, nếu muốn thực hiện quá trình tuần tự

hóa tùy biến, bạn có thể cấu hình đối tượng StreamingContext của formatter

trước khi bắt đầu quá trình tuần tự hóa và giải tuần tự hóa. Tham khảo tài liệu

.NET Framework SDK để có thêm thông tin về lớp StreamingContext.



Ví dụ dưới đây trình bày phiên bản đã được chỉnh sửa của lớp Employee, có hiện thực giao

diện ISerializable. Trong phiên bản này, lớp Employee không tuần tự hóa trường address

nếu đối tượng StreamingContext chỉ định rằng đích của dữ liệu được-tuần-tự-hóa là file.

Phương thức Main sẽ giải thích việc tuần tự hóa và giải tuần tự hóa của một đối tượng

Employee.

using System;

using System.Runtime.Serialization;

[Serializable]

public class Employee : ISerializable {

private string name;

private int age;

private string address;

// Phương thức khởi dựng đơn giản.

public Employee(string name, int age, string address) {



this.name = name;

this.age = age;

this.address = address;

}



612

Chương 16: Các giao diện và mẫu thông dụng



// Phương thức khởi dựng dùng để kích hoạt formatter thực hiện việc

// giải tuần tự hóa một đối tượng Employee. Bạn nên khai báo

// phương thức khởi dựng này là private, hay ít nhất cũng là

// protected để bảo đảm nó không bị gọi quá mức cần thiết.

private Employee(SerializationInfo info, StreamingContext context) {

// Trích xuất tên và tuổi của Employee (sẽ luôn hiện diện

// trong dữ liệu đã-được-tuần-tự-hóa bất chấp giá trị

// của StreamingContext).

name = info.GetString("Name");

age = info.GetInt32("Age");

// Thực hiện trích xuất địa chỉ của Employee

// (thất bại nếu không có).

try {

address = info.GetString("Address");

} catch (SerializationException) {

address = null;

}

}

// Các thuộc tính Name, Age, và Address (đã trình bày ở trên).



§

// Được khai báo bởi giao diện ISerializable, phương thức

// GetObjectData cung cấp cơ chế để formatter thu lấy

// dữ liệu sẽ-được-tuần-tự-hóa.

public void GetObjectData(SerializationInfo inf,

StreamingContext con){



// Luôn tuần tự hóa tên và tuổi của Employee.

inf.AddValue("Name", name);

inf.AddValue("Age", age);



// Không tuần tự hóa địa chỉ của Employee nếu StreamingContext

// cho biết rằng dữ liệu được-tuần-tự-hóa sẽ được ghi ra file.



613

Chương 16: Các giao diện và mẫu thông dụng

if ((con.State & StreamingContextStates.File) == 0) {



inf.AddValue("Address", address);

}

}



// Chép đè Object.ToString để trả về chuỗi mô tả Employee.

public override string ToString() {



StringBuilder str = new StringBuilder();



str.AppendFormat("Name: {0}\n\r", Name);

str.AppendFormat("Age: {0}\n\r", Age);

str.AppendFormat("Address: {0}\n\r", Address);



return str.ToString();

}



public static void Main(string[] args) {



// Tạo một đối tượng Employee mô tả Phuong.

Employee phuong = new Employee("Phuong", 23, "HCM");



// Hiển thị Phuong.

Console.WriteLine(phuong);



// Tuần tự hóa Phuong với đích là một miền ứng dụng khác.

// Địa chỉ của Phuong sẽ được tuần tự hóa.

Stream str = File.Create("phuong.bin");

BinaryFormatter bf = new BinaryFormatter();

bf.Context =

new StreamingContext(StreamingContextStates.CrossAppDomain);

bf.Serialize(str, phuong);

str.Close();



// Giải tuần tự hóa và hiển thị Phuong.

str = File.OpenRead("phuong.bin");



614

Chương 16: Các giao diện và mẫu thông dụng



bf = new BinaryFormatter();

phuong = (Employee)bf.Deserialize(str);

str.Close();

Console.WriteLine(phuong);



// Tuần tự hóa Phuong với đích là file. Trong trường hợp này,

// địa chỉ của Phuong sẽ không được tuần tự hóa.

str = File.Create("phuong.bin");

bf = new BinaryFormatter();

bf.Context = new StreamingContext(StreamingContextStates.File);

bf.Serialize(str, phuong);

str.Close();



// Giải tuần tự hóa và hiển thị Phuong.

str = File.OpenRead("phuong.bin");

bf = new BinaryFormatter();

phuong = (Employee)bf.Deserialize(str);

str.Close();

Console.WriteLine(phuong);



Console.ReadLine();

}



}



2.



Hiện thực kiểu khả-sao-chép (cloneable type)







Bạn cần tạo một kiểu tùy biến cung cấp một cơ chế đơn giản để lập trình viên tạo

bản sao cho các thể hiện của kiểu.







Hiện thực giao diện System.ICloneable.



Khi gán một kiểu giá trị sang một kiểu giá trị khác là bạn đã tạo một bản sao của giá trị đó.

Không có mối liên hệ nào giữa hai giá trị—một thay đổi trên giá trị này sẽ không ảnh hưởng

đến giá trị kia. Tuy nhiên, khi gán một kiểu tham chiếu sang một kiểu tham chiếu khác (ngoại

trừ chuỗi—được bộ thực thi xử lý đặc biệt), bạn không tạo một bản sao mới của kiểu tham

chiếu. Thay vào đó, cả hai kiểu tham chiếu đều chỉ đến cùng một đối tượng, và những thay

đổi trên giá trị của đối tượng đều được phản ánh trong cả hai tham chiếu. Để tạo một bản sao

thật của một kiểu tham chiếu, bạn phải “nhái” lại đối tượng mà nó chỉ đến.



615

Chương 16: Các giao diện và mẫu thông dụng



Giao diện ICloneable nhận dạng một kiểu là khả-sao-chép và khai báo phương thức Clone là

một cơ chế mà thông qua đó, bạn có thể thu lấy bản sao của một đối tượng. Phương thức

Clone không nhận đối số nào và trả về một System.Object, bất chấp kiểu đang hiện thực là gì.

Điều này nghĩa là một khi đã sao một đối tượng, bạn phải ép bản sao về đúng kiểu.

Cách hiện thực phương thức Clone cho một kiểu tùy biến tùy thuộc vào các thành viên dữ liệu

được khai báo bên trong kiểu. Nếu kiểu tùy biến chỉ chứa các thành viên dữ liệu kiểu giá trị

(int, byte...) và System.String, bạn có thể hiện thực phương thức Clone bằng cách tạo một

đối tượng mới và thiết lập các thành viên dữ liệu của nó có giá trị giống như đối tượng hiện

tại. Lớp Object (tất cả các kiểu đều dẫn xuất từ đây) chứa phương thức MemberwiseClone dùng

để tự động hóa quá trình này. Ví dụ dưới đây trình bày một lớp đơn giản có tên là Employee,

chỉ chứa các thành viên chuỗi. Do đó, phương thức Clone dựa vào phương thức thừa kế

MemberwiseClone để tạo bản sao.

using System;



public class Employee : ICloneable {



public string Name;

public string Title;



// Phương thức khởi dựng đơn giản.

public Employee(string name, string title) {



Name = name;

Title = title;

}



// Tạo một bản sao bằng phương thức Object.MemberwiseClone

// vì lớp Employee chỉ chứa các tham chiếu chuỗi.

public object Clone() {



return MemberwiseClone();

}

}



Nếu kiểu tùy biến của bạn có chứa các thành viên dữ liệu kiểu tham chiếu, bạn phải quyết

định xem phương thức Clone của bạn sẽ thực hiện một bản sao cạn (shallow copy) hay một

bản sao sâu (deep copy). Bản sao cạn nghĩa là bất kỳ thành viên dữ liệu kiểu tham chiếu nào

trong bản sao đều sẽ chỉ đến đối tượng giống như thành viên dữ liệu kiểu tham chiếu tương

ứng trong đối tượng gốc. Bản sao sâu nghĩa là bạn phải sao toàn bộ đồ thị đối tượng (object

graph) để các thành viên dữ liệu kiểu tham chiếu của bản sao chỉ đến các bản sao (độc lập về

mặt vật lý) của các đối tượng được tham chiếu bởi đối tượng gốc.



Xem Thêm
Tải bản đầy đủ (.pdf) (706 trang)

×