Ngôn Ngữ Lập Trình C#
Thật vậy, một phương thức được khai báo tường minh thì sẽ không được khai báo với các từ
khóa bổ sung truy cập: abstract, virtual, override, và new.
Một địều quan trọng khác là chúng ta không thể truy cập phương thức thực thi tường minh
thông qua chính đối tượng. Khi chúng ta viết:
theDoc.Read();
Trình biên dịch chỉ hiểu rằng chúng ta thực thi phương thức giao diện ngầm định cho
IStorable. Chỉ một cách duy nhất truy cập các phương thức thực thi tường minh là thông qua
việc gán cho giao diện để thực thi:
ITalk itDoc = theDoc as ITalk;
if ( itDoc != null )
{
itDoc.Read();
}
Sử dụng thực thi tường minh được áp dụng trong ví dụ 8.5
Ví dụ 8.5: Thực thi tường minh.
using System;
interface IStorable
{
void Read();
void Write();
}
interface ITalk
{
void Talk();
void Read();
}
// lớp Document thực thi hai giao diện
public class Document : IStorable, ITalk
{
// bộ khởi dựng
public Document( string s)
{
Console.WriteLine(“Creating document with: {0}”,s);
}
// tạo phương thức ảo
public virtual void Read()
{
Thực Thi Giao Diện
198
.
.
Ngôn Ngữ Lập Trình C#
Console.WriteLine(“Implementing IStorable.Read”);
}
// thực thi bình thường
public void Write()
{
Console.WriteLine(“Implementing IStorable.Write”);
}
// thực thi tường minh
void ITalk.Read()
{
Console.WriteLine(“Implementing ITalk.Read”);
}
public void Talk()
{
Console.WriteLine(“Implementing ITalk.Talk”);
}
}
public class Tester
{
static void Main()
{
// tạo đối tượng Document
Document theDoc = new Document(“Test Document”);
IStorable isDoc = theDoc as IStorable;
if ( isDoc != null )
{
isDoc.Read();
}
ITalk itDoc = theDoc as ITalk;
if ( itDoc != null )
{
itDoc.Read();
}
theDoc.Read();
theDoc.Talk();
}
}
Thực Thi Giao Diện
199
.
.
Ngôn Ngữ Lập Trình C#
Kết quả:
Creating document with: Test Document
Implementing IStorable.Read
Implementing ITalk.Read
Implementing IStorable.Read
Implementing ITalk.Talk
Lựa chọn việc thể hiện phương thức giao diện
Những người thiết kế lớp có thể thu được lợi khi một giao diện được thực thi thông qua thực
thi tường minh và không cho phép các thành phần client của lớp truy cập trừ phi sử dụng
thông qua việc gán cho giao diện.
Giả sử nghĩa của đối tượng Document chỉ ra rằng nó thực thi giao diện IStorable,
nhưng không muốn phương thức Read() và Write() là phần giao diện public của lớp
Document. Chúng ta có thể sử dụng thực thi tường minh để chắc chắn chỉ có thể truy cập
thông qua việc gán cho giao diện. Điều này cho phép chúng ta lưu trữ ngữ nghĩa của lớp
Document trong khi vẫn có thể thực thi được giao diện IStorable. Nếu thành phần client
muốn đối tượng thực thi giao diện IStorable, nó có thể thực hiện gán tường minh cho giao
diện để gọi các phương thức thực thi giao diện. Nhưng khi sử dụng đối tượng Document thì
nghĩa là không có phương thức Read() và Write().
Thật vậy, chúng ta có thể lựa chọn thể hiện những phương thức thông qua thực thi
tường minh, do đó chúng ta có thể trưng bày một vài phương thức thực thi như là một phần
của lớp Document và một số phương thức khác thì không. Trong ví dụ 8.5, đối tượng
Document trưng bày phương thức Talk() như là phương thức của lớp Document, nhưng
phương thức Talk.Read() chỉ được thể hiện thông qua gán cho giao diện. Thậm chí nếu
IStorable không có phương thức Read(), chúng ta cũng có thể chọn thực thi tường minh
phương thức Read() để phương thức không được thể hiện ra bên ngoài như các phương thức
của Document.
Chúng ta lưu ý rằng vì thực thi giao diện tường minh ngăn ngừa việc sử dụng từ khóa
virtual, một lớp dẫn xuất có thể được hỗ trợ để thực thi lại phương thức. Do đó, nếu Note
dẫn xuất từ Document, nó có thể được thực thi lại phương thức Talk.Read() bởi vì lớp
Document thực thi phương thức Talk.Read() không phải ảo.
Ẩ n thành viên
Ngôn ngữ C# cho phép ẩn các thành viên của giao diện. Ví dụ, chúng ta có một giao diện
IBase với một thuộc tính P:
interface IBase
Thực Thi Giao Diện
200
.
.
Ngôn Ngữ Lập Trình C#
{
int P { get; set;}
}
và sau đó chúng ta dẫn xuất từ giao diện này ra một giao diện khác, IDerived, giao diện mới
này làm ẩn thuộc tính P với một phương thức mới P():
interface IDerived : IBase
{
new int P();
}
Việc cài đặt này là một ý tưởng tốt, bây giờ chúng ta có thể ẩn thuộc tính P trong lớp cơ sở.
Một thực thi của giao diện dẫn xuất này đòi hỏi tối thiểu một thành viên giao diện tường
minh. Chúng ta có thể sử dụng thực thi tường minh cho thuộc tính của lớp cơ sở hoặc của
phương thức dẫn xuất, hoặc chúng ta có thể sử dụng thực thi tường minh cho cả hai. Do đó,
ba phiên bản được viết sau đều hợp lệ:
class myClass : IDerived
{
// thực thi tường minh cho thuộc tính cơ sở
int IBase.p { get{ }}
// thực thi ngầm định phương thức dẫn xuất
public int P() { }
}
class myClass : IDerived
{
// thực thi ngầm định cho thuộc tính cơ sở
public int P { get{ }}
// thực thi tường minh phương thức dẫn xuất
int IDerived.P() { }
}
class myClass : IDerived
{
// thực thi tường minh cho thuộc tính cơ sở
int IBase.P { get{ }}
// thực thi tường minh phương thức dẫn xuất
int IDerived.P(){ }
}
Truy cập lớp không cho dẫn xuất và kiểu giá trị
Thực Thi Giao Diện
201
.
.
Ngôn Ngữ Lập Trình C#
Nói chung, việc truy cập những phương thức của một giao diện thông qua việc gán cho giao
diện thì thường được thích hơn. Ngoại trừ đối với kiểu giá trị (như cấu trúc) hoặc với các lớp
không cho dẫn xuất (sealed class). Trong trường hợp này, cách ưu chuộng hơn là gọi phương
thức giao diện thông qua đối tượng.
Khi chúng ta thực thi một giao diện trong một cấu trúc, là chúng ta đang thực thi nó trong một
kiểu dữ liệu giá trị. Khi chúng ta gán cho môt tham chiếu giao diện, có một boxing ngầm định
của đối tượng. Chẳng may khi chúng ta sử dụng giao diện để bổ sung đối tượng, nó là một đối
tượng đã boxing, không phải là đối tượng nguyên thủy cần được bổ sung. Xa hơn nữa, nếu
chúng ta thay đổi kiểu dữ liệu giá trị, thì kiểu dữ liệu được boxing vẫn không thay đổi. Ví dụ
8.6 tạo ra một cấu trúc và thực thi một giao diện IStorable và minh họa việc boxing ngầm
định khi gán một cấu trúc cho một tham chiếu giao diện.
Ví dụ 8.6: Tham chiếu đến kiểu dữ liệu giá trị.
using System;
// khai báo một giao diện đơn
interface IStorable
{
void Read();
int Status { get; set;}
}
// thực thi thông qua cấu trúc
public struct myStruct : IStorable
{
public void Read()
{
Console.WriteLine(“Implementing IStorable.Read”);
}
public int Status
{
get
{
return status;
}
set
{
status = value;
}
}
Thực Thi Giao Diện
202
.
.