Giáo trình hướng dẫn cách truy cập vào các mảng đa chiều trên diện rộng có các kích thước khác nhau phần 5 pdf

40 312 0
Giáo trình hướng dẫn cách truy cập vào các mảng đa chiều trên diện rộng có các kích thước khác nhau phần 5 pdf

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Ngơn Ngữ Lập Trình C# } Sự chuyển đổi thực cách ngầm định số nguyên chuyển thành phân số cách thiết lập tử số giá trị số nguyên mẫu số có giá trị Việc thực giao lại cho phương thức khởi dựng lấy tham số Toán tử chuyển đổi thứ hai thực cách tường minh, chuyển từ Fraction số nguyên: public static explicit operator int( Fraction theFraction ) { return theFraction.numerator / theFraction.denominator; } Bởi ví dụ sử dụng phép chia nguyên, phép chia cắt bỏ phần phân lấy phần nguyên Do phân số có giá trị 16/15 kết số nguyên trả Một số phép chuyển đổi tốt cách sử dụng làm tròn số Tiếp theo sau toán tử so sánh (==) tốn tử so sánh khơng (!=) Chúng ta nên nhớ thực thi toán tử so sánh phải thực thi tốn tử so sánh không Chúng ta định nghĩa giá trị hai Fraction tử số tử số mẫu số mẫu số Vi dụ, hai phân số 3/4 6/8 khơng so sánh Một lần nữa, thực thi tốt tối giản tử số mẫu số 6/8 đơn giản thành 3/4 so sánh hai phân số Trong lớp thực thi phủ phương thức Equals() lớp object, đối tượng Fraction đối xử cách đa hình với đối tượng khác Trong phần thực thi phương thức ủy thác việc so sánh lại cho toán tử so sánh cách gọi tốn tử (==) Lớp Fraction thực thi hết tất toán tử số học cộng, trừ, nhân, chia Tuy nhiên, phạm vi nhỏ hẹp minh họa thực thi toán tử cộng, chí phép cộng thực đơn giản Chúng ta thử nhìn lại, hai mẫu số ta cộng tử số: public static Fraction operator + ( Fraction lhs, Fraction rhs) { if ( lhs.denominator == rhs.denominator) { return new Fraction( lhs.numerator + rhs.numerator, lhs.denominator); } } Nếu mẫu số khơng nhau, thực nhân chéo: int firstProduct = lhs.numerator * rhs.denominator; int secondProduct = rhs.numerator * lhs.denominator; return new Fraction( firstProduct + secondProduct, lhs.denominator * 161 Nạp Chồng Tốn Tử Ngơn Ngữ Lập Trình C# rhs.denominator); Cuối phủ phương thức ToString() lớp object, phương thức thực viết xuất nội dung phân số dạng : tử số / mẫu số: public override string ToString() { string s = numerator.ToString() + “/” + denominator.ToString(); return s; } Chúng ta tạo chuỗi cách gọi phương thức ToString() numerator Do numerator đối tượng, nên trình biên dịch ngầm định thực boxing số nguyên numerator sau gọi phương thức ToString(), trả chuỗi thể giá trị số nguyên numerator Sau ta nối chuỗi với “/” cuối chuỗi thể giá trị mẫu số Với lớp Fraction tạo ra, thực kiểm tra lớp Đầu tiên tạo hai phân số 3/4, 2/4: Fraction f1 = new Fraction( 3, 4); Console.WriteLine("f1:{0}",f1.ToString()); Fraction f2 = new Fraction( 2, 4); Console.WriteLine("f2:{0}",f2.ToString()); Kết thực lệnh sau: In Fraction Constructor(int, int) f1: 3/4 In Fraction Constructor(int, int) f2: 2/4 Do phương phức khởi dựng lớp Fraction có gọi hàm WriteLine() để xuất thông tin khởi dựng nên tạo đối tượng (new) thơng tin hịển thị Dòng hàm Main() gọi toán tử cộng, phương thức tĩnh Mục đích tốn tử cộng hai phân số trả phân số tổng hai phân số đưa vào: Fraction f3 = f1 + f2; Console.WriteLine(“f1 + f2 = f3: {0}”, f3.ToString()); Hai câu lệnh cho kết sau: In operator + In Fraction Constructor( int, int) f1 + f2 = f3: 5/4 Toán tử + gọi trước sau đến phương thức khởi dựng đối tượng f3 Phương thức khởi dựng lấy hai tham số nguyên để tạo tử số mẫu số phân số f3 162 Nạp Chồng Tốn Tử Ngơn Ngữ Lập Trình C# Hai câu lệnh cộng giá trị nguyên vào phân số f3 gán kết cho phân số f4: Fraction f4 = f3 + 5; Console.WriteLine(“f3 + = f4: {0}”, f4.ToString()); Kết trình bày theo thứ tự sau: In implicit conversion to Fraction In Fraction Construction(int) In operator+ In Fraction Constructor(int, int) f3 + = f4: 25/4 Ghi chú: toán tử chuyển đổi ngầm định gọi chuyển thành phân số Phân số tạo từ toán tử chuyển đổi ngầm định gọi phương thức khởi dựng tham số để tạo phân số 5/1 Phân số chuyển thành toán hạng phép cộng với phân số f3 kết trả phân số f4 tổng hai phân số Thử nghiệm cuối tạo phân số f5, sau gọi tốn tử nạp chồng so sánh để kiểm tra xem hai phân số có hay không Câu hỏi trả lời Câu hỏi 1: Có phải xây dựng lớp cần dùng nạp chồng toán tử với chức tính tốn ? Trả lời 1: Đúng vậy, việc thực nạp chồng toán tử tự nhiên trực quan Tuy nhiên số ngôn ngữ NET VB.NET khơng hỗ trợ việc nạp chồng tốn tử nên, tốt muốn cho lớp C# gọi từ ngơn ngữ khác khơng hỗ trợ nạp chồng tốn tử nên xây dựng phương thức tương đương để thực chức như: Add, Sub, Mul, Câu hỏi 2: Những điều lưu ý sử dụng nạp chồng toán tử lớp? Trả lời 2: Nói chung thật cần thiết gây nhầm lẫn Ví dụ ta xây dựng lớp Employee có nhiều thuộc tính số lương, thâm niên, tuổi Chúng ta muốn xây dựng toán tử ++ cho lương làm nhầm lẫn với việc tăng số năm công tác, hay tăng tuổi Do việc sử dụng nạp chồng toán tử phải cân nhắc tránh gây nhầm lẫn Tốt sử dụng lớp có thuộc tính số Câu hỏi 3: Khi xây dựng tốn tử so sánh có phải cần dùng toán tử so sánh bằng? Trả lời 3: Đúng cần dùng toán tử so sánh tạo tốn tử so sánh mà thơi Tuy nhiên, tốt nên xây dựng thêm toán tử so sánh khác như: so sánh khác, so sánh nhỏ hơn, so sánh lớn Việc làm cho lớp hoàn thiện Câu hỏi thêm 163 Nạp Chồng Tốn Tử Ngơn Ngữ Lập Trình C# Câu hỏi 1: Khi sử dụng tốn tử chuyển đổi? Thế chuyển đổi tường minh chuyển đổi ngầm định? Câu hỏi 2: Có thể tạo ký toán tử riêng ta thực thi nạp chồng tốn tử hay khơng? Câu hỏi 3: Có tốn tử mà NET quy định? Ký hiệu toán tử? Bài tập Bài tập 1: Hãy tiếp tục phát triển lớp Fraction ví dụ chương cách thêm tốn tử khác trừ, nhân, chia, so sánh Bài tập 2: Xây dựng lớp điểm không gian hai chiều, với toán tử cộng, trừ, nhân, chia Bài tập 3: Tương tự tập điểm nằm không gian chiều Bài tập 4: Xây dựng lớp số phúc (số ảo) với phép toán cộng, trừ, nhân, chia 164 Nạp Chồng Tốn Tử Ngơn Ngữ Lập Trình C# Chương CẤU TRÚC  Định nghĩa cấu trúc  Tạo cấu trúc  Cấu trúc kiểu giá trị  Gọi khởi dựng mặc định  Tạo cấu trúc không gọi new  Câu hỏi & tập Cấu trúc kiểu liệu đơn giản người dùng định nghĩa, kích thước nhỏ dùng để thay cho lớp Những cấu trúc tương tự lớp chứa phương thức, thuộc tính, trường, tốn tử, kiểu liệu lồng bên mục (indexer) Có số khác quan trọng lớp cấu trúc Ví dụ, cấu trúc khơng hỗ trợ kế thừa hủy giống kiểu lớp Một điều quan trọng lớp kiểu liệu tham chiếu, cấu trúc kiểu lịêu giá trị (Chương thảo luận kiểu liệu tham chiếu kiểu liệu giá trị) Do cấu trúc thường dùng để thể hiển đối tượng khơng địi hỏi ngữ nghĩa tham chiếu, hay lớp nhỏ mà đặt vào stack có lợi đặt nhớ heap Một nhận xét rút nên sử dụng cấu trúc với kiểu liệu nhỏ, hành vi hay thuộc tính giống kiểu liệu xây dựng sẵn Cấu trúc có hiệu sử dụng chúng mảng nhớ (Chương 9) Tuy nhiên, cấu trúc hiệu sử dụng dạng tập hợp (collections) Tập hợp xây dựng hướng tới kiểu liệu tham chiếu Trong chương tìm hiểu định nghĩa làm việc với kiểu cấu trúc cách sử dụng khởi dựng để khởi tạo giá trị cấu trúc Định nghĩa cấu trúc Cú pháp để khai báo cấu trúc tương tự cách khai báo lớp: [thuộc tính] [bổ sung truy cập] struct [: danh sách giao diện] { [thành viên cấu trúc] 165 Cấu Trúc Ngơn Ngữ Lập Trình C# } Ví dụ 7.1 sau minh họa cách tạo cấu trúc Kiểu Location thể điểm không gian hai chiều Lưu ý cấu trúc Location khai báo xác thực khai báo với lớp, ngoại trừ việc sử dụng từ khóa struct Ngồi lưu ý hàm khởi dựng Location lấy hai số nguyên gán giá trị chúng cho biến thành viên, x y Tọa độ x y Location khai báo thuộc tính  Ví dụ 7.1 Tạo cấu trúc using System; public struct Location { public Location( int xCoordinate, int yCoordinate) { xVal = xCoordinate; yVal = yCoordinate; } public int x { get { return xVal; } set { xVal = value; } } public int y { get { return yVal; } set { yVal = value; } } 166 Cấu Trúc Ngơn Ngữ Lập Trình C# public override string ToString() { return (String.Format(“{0}, {1}”, xVal, yVal)); } // thuộc tính private lưu toạ độ x, y private int xVal; private int yVal; } public class Tester { public void myFunc( Location loc) { loc.x = 50; loc.y = 100; Console.WriteLine(“Loc1 location: {0}”, loc); } static void Main() { Location loc1 = new Location( 200, 300); Console.WriteLine(“Loc1 location: {0}”, loc1); Tester t = new Tester(); t.myFunc( loc1 ); Console.WriteLine(“Loc1 location: {0}”, loc1); } } Không giống lớp, cấu trúc không hỗ trợ việc thừa kế Chúng thừa kế ngầm định từ lớp object (tương tự tất kiểu liệu C#, bao gồm kiểu liệu xây dựng sẵn) kế thừa từ lớp khác hay cấu trúc khác Cấu trúc ngầm định sealed, điều có ý nghĩa khơng có lớp hay cấu trúc dẫn xuất từ Tuy nhiên, giống lớp, cấu trúc thực thi nhiều giao diện Sau số khác là:  Khơng có hủy khởi tạo mặc định tùy chọn: Những cấu trúc khơng có hủy khơng có khởi tạo mặc định không tham số tùy chọn Nếu không cung cấp khởi tạo cấu trúc cung cấp khởi tạo mặc định, giá trị thiết lập cho tất liệu thành viên hay giá trị mặc định tương ứng cho kiểu liệu (bảng 4.2) Nếu cung cấp khởi dựng phải khởi tạo tất trường cấu trúc 167 Cấu Trúc Ngơn Ngữ Lập Trình C#  Khơng cho phép khởi tạo: khởi tạo trường thể (instance fields) cấu trúc, đoạn mã nguồn sau khơng hợp lệ: private int xVal = 20; private int yVal = 50; điều thực tốt lớp Cấu trúc thiết kế hướng tới đơn giản gọn nhẹ Trong liệu thành viên private hỗ trợ việc che dấu liệu đóng gói Một vài người lập trình có cảm giác điều phá hỏng cấu trúc Họ tạo liệu thành viên public, đơn giản thực thi cấu trúc Những người lập trình khác có cảm giác thuộc tính cung cấp giao diện rõ ràng, đơn giản việc thực lập trình tốt địi hỏi phải che dấu liệu chí với liệu đơn giản Chúng ta chọn cách nào, nói chung phụ thuộc vào quan nệm thiết kế người lập trình Dù chọn cách ngơn ngữ C# hỗ trợ hai cách tiếp cận Tạo cấu trúc Chúng ta tạo thể cấu trúc cách sử dụng từ khóa new câu lệnh gán, tạo đối tượng lớp Như ví dụ 7.1, lớp Tester tạo thể Location sau: Location loc1 = new Location( 200, 300); Ở thể tên loc1 truyền hai giá trị 200 300 Cấu trúc kiểu giá trị Phần định nghĩa lớp Tester ví dụ 7.1 bao gồm đối tượng Location loc1 tạo với giá trị 200 300 Dòng lệnh sau gọi thực khởi tạo cấu trúc Location: Location loc1 = new Location( 200, 300); Sau phương tức WriteLine() gọi: Console.WriteLine(“Loc1 location: {0}”, loc1); Dĩ nhiên WriteLine chờ đợi đối tượng, Location cấu trúc (một kiểu giá trị) Trình biên dịch tự động boxing cấu trúc (cũng giống trình biên dịch làm với kiểu liệu giá trị khác) Một đối tượng sau boxing truyền vào cho phương thức WriteLine() Tiếp sau phương thức ToString() gọi đối tượng boxing này, cấu trúc ngầm định kế thừa từ lớp object, đáp ứng đa hình, cách phủ phương thức đối tượng khác Loc1 location 200, 300 Tuy nhiên cấu trúc kiểu giá trị, nên truyền vào hàm, chúng truyền giá trị vào hàm Cũng ta thấy dịng lệnh kế tiếp, đối tượng Location truyền vào phương thức myFunc(): t.myFunc( loc1 ); 168 Cấu Trúc Ngơn Ngữ Lập Trình C# Trong phương thức myFunc() hai giá trị gán cho x y, sau giá trị xuất hình: Loc1 location: 50, 100 Khi phương thức myFunc() trả cho hàm gọi ( Main()) gọi tiếp phương thức WriteLine() lần giá trị khơng thay đổi: Loc1 location: 200, 300 Như cấu trúc truyền vào hàm đối tượng giá trị, tạo bên phương thức myFunc() Nếu thử đổi khai báo Location class sau: public class Location Sau chạy lại chương trình có kết quả: Loc1 location: 200, 3000 In myFunc loc: 50, 100 Loc1 location: 50, 100 Lúc Location đối tượng tham chiếu nên truyền vào phương thức myFunc() việc gán giá trị cho x y điều làm thay đổi đối tượng Location Gọi khởi dựng mặc định Như đề cập phần trước, khơng tạo khởi dựng khởi dựng mặc định ngầm định trình biên dịch tạo Chúng ta nhìn thấy điều bỏ khởi dựng tạo ra: /*public Location( int xCoordinate , int yCoordinate) { xVal = xCoordinate; yVal = yCoordinate; } */ ta thay dòng lệnh hàm Main() tạo Location có hai tham số câu lệnh tạo không tham số sau: //Location loc1 = new Location( 200, 300) Location loc1 = new Location(); Bởi lúc khơng có phương thức khởi dựng khai báo, phương thức khởi dựng ngầm định gọi Kết thực giống sau: Loc1 location 0, In myFunc loc: 50, 100 Loc1 location: 0, Bộ khởi tạo mặc định thiết lập tất biến thành viên với giá trị 169 Cấu Trúc Ngôn Ngữ Lập Trình C# Ghi chú: Đối với lập trình viên C++ lưu ý, ngơn ngữ C#, từ khóa new khơng phải luôn tạo đối tượng nhớ heap Các lớp tạo heap, cấu trúc tạo stack Ngồi ra, new bỏ qua (sẽ bàn tiếp phần sau), khởi dựng khơng gọi Do ngơn ngữ C# u cầu phải có phép gán trước sử dụng, phải khởi tạo tường minh tất biến thành viên trước sử dụng chúng cấu trúc Tạo cấu trúc không gọi new Bởi Location cấu trúc khơng phải lớp, thể tạo stack Trong ví dụ 7.1 tốn tử new gọi: Location loc1 = new Location( 200, 300); kết đối tượng Location tạo stack Tuy nhiên, toán tử new gọi khởi dựng lớp Location, không giống với lớp, cấu trúc tạo mà khơng cần phải gọi toán tử new Điều giống biến kiểu liệu xây dựng sẵn (như int, long, char, ) tạo Ví dụ 7.2 sau minh họa việc tạo cấu trúc không sử dụng toán tử new Ghi chú: Đây khuyến cáo, ví dụ sau minh họa cách tạo cấu trúc mà khơng phải sử dụng tốn tử new có khác C# ngơn ngữ C++ khác cách ngôn ngữ C# đối xử với lớp khác cấu trúc Tuy nhiên, việc tạo cấu trúc mà khơng dùng từ khóa new khơng có lợi tạo chương trình khó hiểu, tiềm ẩn nhiều lỗi, khó trì Chương trình họa sau khơng khuyến khích  Ví dụ 7.2: Tạo cấu trúc mà không sử dụng new using System; public struct Location { public Location( int xCoordinate, int yCoordinate) { xVal = xCoordinate; yVal = yCoordinate; } public int x { get { return xVal; } 170 Cấu Trúc Ngơn Ngữ Lập Trình C# }  Kết quả: Creating document with: Test Document Implementing the Read Method for IStorable Implementing Compress Implementing LogSavedBytes Implementing Compress Implementing LogOriginalSize Implementing LogSaveBytes Implementing Compress Implementing the Read Method for IStorable Implementing Encrypt Ví dụ 8.2 bắt đầu việc thực thi giao diện IStorable giao diện ICompressible Sau phần mở rộng đến giao diện ILoggedCompressible sau kết hợp hai vào giao diện IStorableCompressible Và giao diện cuối ví dụ IEncrypt Chương trình Tester tạo đối tượng Document sau gán vào giao diện khác Khi đối tượng gán cho giao diện ILoggedCompressible, dùng giao diện để gọi phương thức giao diện ICompressible ILoggedCompressible mở rộng thừa kế phương thức từ giao diện sở: ILoggedCompressible ilcDoc = doc as ILoggedCompressible; if ( ilcDoc != null ) { ilcDoc.LogSavedBytes(); ilcDoc.Compress(); // ilcDoc.Read(); // gọi } Tuy nhiên, gọi phương thức Read() phương thức giao diện IStorable, không liên quan đến giao diện Nếu thêm lệnh vào chương trình biên dịch lỗi Nếu gán vào giao diện IStorableCompressible, giao diện kết hợp hai giao diện IStorable giao diện ICompressible, gọi tất phương thức IStorableCompressible, ICompressible, IStorable: IStorableCompressible isc = doc as IStorableCompressible; if ( isc != null ) { isc.LogOriginalSize(); // IStorableCompressible 186 Thực Thi Giao Diện Ngôn Ngữ Lập Trình C# isc.LogSaveBytes(); // ILoggedCompressible isc.Compress(); // ICompress isc.Read(); // IStorable } Truy cập phương thức giao diện Chúng ta truy cập thành viên giao diện IStorable thể thành viên lớp Document: Document doc = new Document(“Test Document”); doc.status = -1; doc.Read(); ta tạo thể giao diện cách gán đối tượng Document cho kiểu liệu giao diện, sau sử dụng giao diện để truy cập phương thức: IStorable isDoc = (IStorable) doc; isDoc.status = 0; isDoc.Read(); Ghi chú: Cũng nói trước đây, khơng thể tạo thể giao diện cách trực tiếp.Do khơng thể thực sau: IStorable isDoc = new IStorable(); Tuy nhiên tạo thể lớp thực thi sau: Document doc = new Document(“Test Document”); Sau tạo thể giao diện cách gán đối tượng thực thi đến kiểu liệu giao diện, trường hợp IStorable: IStorable isDoc = (IStorable) doc; Chúng ta kết hợp bước sau: IStorable isDoc = (IStorable) new Document(“Test Document”); Nói chung, cách thiết kế tốt định truy cập phương thức giao diện thông qua tham chiếu giao diện Do cách tốt sử dụng isDoc.Read(), sử dụng doc.Read() ví dụ trước Truy cập thơng qua giao diện cho phép đối xử giao diện cách đa hình Nói cách khác, tạo hai hay nhiều lớp thực thi giao diện, sau cách truy cập lớp thông qua giao diện Gán đối tượng cho giao diện Trong nhiều trường hợp, trước đối tượng có hỗ trợ giao diện đưa Ví dụ, giả sử có tập hợp đối tượng Document, vài đối tượng lưu trữ số cịn lại chưa Và giả sử thêm giao diện giao diện thứ hai, ICompressible cho đối tượng để nén liệu truyền qua mail nhanh chóng: interface ICompressible { 187 Thực Thi Giao Diện Ngơn Ngữ Lập Trình C# void Compress(); void Decompress(); } Nếu đưa kiểu Document, ta khơng biết lớp có hỗ trợ giao diện IStorable hay ICompressible hai Ta có đoạn chương trình sau: Document doc = new Document(“Test Document”); IStorable isDoc = (IStorable) doc; isDoc.Read(); ICompressible icDoc = (ICompressible) doc; icDoc.Compress(); Nếu Document thực thi giao diện IStorable: public class Document : IStorable phép gán cho ICompressible biên dịch ICompressible giao diện hợp lệ Tuy nhiên, phép gán không hợp lệ nên chương trình chạy tạo ngoại lệ (exception): A exception of type System.InvalidCastException was thrown Phần ngoại lệ trình bày Chương 11 Toán tử is Chúng ta muốn kiểm tra đối tượng xem có hỗ trợ giao diện, để sau thực phương thức tương ứng Trong ngơn ngữ C# có hai cách để thực điều Phương pháp sử dụng toán tử is Cú pháp toán tử is là: is Toán tử is trả giá trị true biểu thức thường kiểu tham chiếu gán an tồn đến kiểu liệu cần kiểm tra mà không phát sinh ngoại lệ Ví dụ 8.3 minh họa việc sử dụng tốn tử is để kiểm tra Document có thực thi giao diện IStorable hay ICompressible  Ví dụ 8.3: Sử dụng toán tử is using System; interface IStorable { void Read(); void Write(object obj); int Status { get; set; } } // giao diện 188 Thực Thi Giao Diện Ngôn Ngữ Lập Trình C# interface ICompressible { void Compress(); void Decompress(); } // Document thực thi IStorable public class Document : IStorable { public Document( string s) { Console.WriteLine(“Creating document with: {0}”, s); } // IStorable public void Read() { Console.WriteLine(“Implementing the Read Method for IStorable”); } // IStorable.WriteLine() public void Write( object o) { Console.WriteLine(“Implementing the Write Method for IStorable”); } // IStorable.Status public int Status { get { return status; } set { status = value; } } // bien vien luu gia tri cua thuoc tinh Status private int status = 0; } public class Tester 189 Thực Thi Giao Diện Ngơn Ngữ Lập Trình C# { static void Main() { Document doc = new Document(“Test Document”); // gán an toàn if ( doc is IStorable ) { IStorable isDoc = (IStorable) doc; isDoc.Read(); } // việc kiểm tra sai if ( doc is ICompressible ) { ICompressible icDoc = (ICompressible) doc; icDoc.Compress(); } } } Trong ví dụ 8.3, hàm Main() lúc thực việc gán với interface kiểm tra hợp lệ Việc kiểm tra thực câu lệnh if: if ( doc is IStorable ) Biểu thức điều kiện trả giá trị true phép gán thực đối tượng có thực thi giao diện bên phải tốn tử is Tuy nhiên, việc sử dụng toán tử is đưa việc khơng có hiệu Để hiểu điều này, xem đoạn chương trình biên dịch mã IL Ở có ngoại lệ nhỏ, dòng bên sử dụng hệ thập lục phân: IL_0023: isinst ICompressible IL_0028: brfalse.s IL_0039 IL_002a: ldloc.0 IL_002b: castclass ICompressible IL_0030: stloc.2 IL_0031: ldloc.2 IL_0032: callvirt instance void ICompressible::Compress() IL_0037: br.s IL_0043 IL_0039: ldstr “Compressible not supported” Điều quan trọng xảy phép kiểm tra ICompressible dịng 23 Từ khóa isinst mã MSIL tương ứng với toán tử is Nếu việc kiểm tra đối tượng (doc) kiểu kiểu bên 190 Thực Thi Giao Diện Ngơn Ngữ Lập Trình C# phải Thì chương trình chuyển đến dịng lệnh 2b để thực tiếp castclass gọi Điều không may castcall kiểm tra kiểu đối tượng Do việc kiểm tra thực hai lần Giải pháp hiệu việc sử dụng toán tử as Toán tử as Toán tử as kết hợp toán tử is phép gán cách kiểm tra hợp lệ phép gán (kiểm tra toán tử is trả true) sau phép gán thực Nếu phép gán không hợp lệ (khi phép gán trả ề giá trị false), tốn tử as trả giá trị null Ghi chú: Từ khóa null thể tham chiếu không tham chiếu đến đâu (null reference) Đối tượng có giá trị null tức không tham chiếu đến đối tượng Sử dụng toán tử as để loại bỏ việc thực xử lý ngoại lệ Đồng thời né tránh việc thực kiểm tra dư thừa hai lần Do vậy, việc sử dụng tối ưu phép gán cho giao diện sử dụng as Cú pháp sử dụng toán tử as sau: as Đoạn chương trình sau thay việc sử dụng toán tử is toán tử as sau thực việc kiểm tra xem giao diện gán có null hay khơng: static void Main() { Document doc = new Document(“Test Document”); IStorable isDoc = doc as IStorable; if ( isDoc != null ) { isDoc.Read(); } else { Console.WriteLine(“IStorable not supported”); } ICompressible icDoc = doc as ICompressible; if ( icDoc != null) { icDoc.Compress(); } else { Console.WriteLine(“Compressible not supported”); } 191 Thực Thi Giao Diện Ngơn Ngữ Lập Trình C# } Ta so sánh đoạn mã IL sau với đoạn mã IL sử dụng toán tử is trước thấy đoạn mã sau có nhiều hiệu hơn: IL_0023: isinst ICompressible IL_0028: stloc.2 IL_0029: ldloc.2 IL_002a: brfalse.s IL_0034 IL_002c: ldloc.2 IL_002d: callvirt instance void ICompressible::Compress() Ghi chú: Nếu mục đích kiểm tra đối tượng có hỗ trợ giao diện sau thực việc gán cho giao diện, cách tốt sử dụng toán tử as hiệu Tuy nhiên, muốn kiểm tra kiểu liệu khơng thực phép gán lúc Có lẽ muốn thực việc kiểm tra không thực việc gán, đơn giản muốn thêm vào danh sách chúng thực giao diện Trong trường hợp này, sử dụng toán tử is cách lựa chọn tốt Giao diện đối lập với lớp trừu tượng Giao diện giống lớp trừu tượng Thật vậy, thay khai báo IStorable trở thành lớp trừu tượng: abstract class Storable { abstract public void Read(); abstract public void Write(); } Bây lớp Document thừa kế từ lớp trừu tượng IStorable, khơng có khác nhiều so với việc sử dụng giao diện Tuy nhiên, giả sử mua lớp List từ hãng thứ ba muốn kết hợp với lớp có sẵn Storable Trong ngơn ngữ C++ tạo lớp StorableList kế thừa từ List Storable Nhưng ngôn ngữ C# làm được, kế thừa từ lớp trừu tượng Storable từ lớp List C# khơng cho phép thực đa kế thừa từ lớp Tuy nhiên, ngôn ngữ C# cho phép thực thi giao diện dẫn xuất từ lớp sở Do đó, cách làm cho Storable giao diện, kế thừa từ lớp List từ IStorable Ta tạo lớp StorableList sau: public class StorableList : List, IStorable { // phương thức List 192 Thực Thi Giao Diện Ngôn Ngữ Lập Trình C# public void Read() { } public void Write( object o) { } // } Thực thi phủ giao diện Khi thực thi lớp tự đánh dấu hay tất phương thức thực thi giao diện phương thức ảo Ví dụ, lớp Document thực thi giao diện IStorable đánh dấu phương thức Read() Write() phương thức ảo Lớp Document đọc viết nội dung vào kiểu liệu File Những người phát triển sau dẫn xuất kiểu liệu từ lớp Document, lớp Note hay lớp EmailMessage, người mong muốn lớp Note đọc viết vào sở liệu vào tập tin Ví dụ 8.4 mở rộng từ ví dụ 8.3 minh họa việc phủ thực thi giao diện Phương thức Read() đánh dấu phương thức ảo thực thi Document.Read() cuối phủ kiểu liệu Note dẫn xuất từ Document  Ví dụ 8.4: Phủ thực thi giao diện using System; interface IStorable { void Read(); void Write(); } // lớp Document đơn giản thực thi giao diện IStorable public class Document : IStorable { // khởi dựng public Document( string s) { Console.WriteLine(“Creating document with: {0}”, s); } // đánh dấu phương thức Read ảo public virtual void Read() { Console.WriteLine(“Document Read Method for IStorable”); 193 Thực Thi Giao Diện Ngôn Ngữ Lập Trình C# } // khơng phải phương thức ảo public void Write() { Console.WriteLine(“Document Write Method for IStorable”); } } // lớp dẫn xuất từ Document public class Note : Document { public Note( string s) : base(s) { Console.WriteLine(“Creating note with: {0}”, s); } // phủ phương thức Read() public override void Read() { Console.WriteLine(“Overriding the Read Method for Note!”); } // thực thi phương thức Write riêng lớp public void Write() { Console.WriteLine(“Implementing the Write method for Note!”); } } public class Tester { static void Main() { // tạo đối tượng Document Document theNote = new Note(“Test Note”); IStorable isNote = theNote as IStorable; if ( isNote != null) { isNote.Read(); isNote.Write(); } Console.WriteLine(“\n”); 194 Thực Thi Giao Diện Ngơn Ngữ Lập Trình C# // trực tiếp gọi phương thức theNote.Read(); theNote.Write(); Console.WriteLine(“\n”); // tạo đối tượng Note Note note2 = new Note(“Second Test”); IStorable isNote2 = note2 as IStorable; if ( isNote != null ) { isNote2.Read(); isNote2.Write(); } Console.WriteLine(“\n”); // trực tiếp gọi phương thức note2.Read(); note2.Write(); } }  Kết quả: Creating document with: Test Note Creating note with: Test Note Overriding the Read method for Note! Document Write Method for IStorable Overriding the Read method for Note! Document Write Method for IStorable Creating document with: Second Test Creating note with: Second Test Overriding the Read method for Note! Document Write Method for IStorable Overriding the Read method for Note! Implementing the Write method for Note! Trong ví dụ trên, lớp Document thực thi giao diện đơn giản IStorable: interface IStorable 195 Thực Thi Giao Diện Ngơn Ngữ Lập Trình C# { void Read(); void Write(); } Người thiết kế lớp Document thực thi phương thức Read() phương thức ảo không tạo phương thức Write() tương tự vậy: public virtual void Read() Trong ứng dụng giới thực, đánh dấu hai phương thức phương thức ảo Tuy nhiên ví dụ minh họa việc người phát triển tùy ý chọn phương thức ảo giao diện mà lớp thực thi Một lớp Note dẫn xuất từ Document: public class Note : Document Việc phủ phương thức Read() lớp Note không cần thiết, ta tự làm điều này: public override void Read() Trong lớp Tester, phương thức Read() Write() gọi theo bốn cách sau:  Thông qua lớp sở tham chiếu đến đối tượng lớp dẫn xuất  Thông qua giao diện tạo từ lớp sở tham chiếu đến đối tượng dẫn xuất  Thông qua đối tượng dẫn xuất  Thông qua giao diện tạo từ đối tượng dẫn xuất Thực cách gọi thứ nhất, tham chiếu Document tạo ra, địa đối tượng lớp dẫn xuất Note tạo heap gán trở lại cho đối tượng Document: Document theNote = new Note(“Test Note”); Môt tham chiếu giao diện tạo toán tử as sử dụng để gán Document cho tham chiếu giao diện IStorable: IStorable isNote = theNote as IStorable; Sau gọi phương thức Read() Write() thông qua giao diện Kết xuất phương thức Read() thực cách đa hình phương thức Write() khơng, ta có kết xuất sau: Overriding the Read method for Note! Document Write Method for IStorable Phương thức Read() Write() gọi trực tiếp từ thân đối tượng: theNote.Read(); theNote.Write(); lần thấy việc thực thi đa hình làm việc: Overriding the Read method for Note! Document Write Method for IStorable 196 Thực Thi Giao Diện Ngôn Ngữ Lập Trình C# Trong trường hợp này, phương thức Read() lớp Note gọi, phương thức Write() lớp Document gọi Để chứng tỏ kết phương thức phủ quyết, tiếp tục tạo đối tượng Note thứ hai lúc ta gán cho tham chiếu Note Điều sử dụng để minh họa cho trường hợp cuối (gọi thông qua đối tượng dẫn xuất gọi thông qua giao diện tạo từ đối tượng dẫn xuất): Note note2 = new Note(“Second Test”); Một lần nữa, gán cho tham chiếu, phương thức phủ Read() gọi Tuy nhiên, phương thức gọi trực tiếp từ đối tượng Note: note2.Read(); note2.Write(); kết cho ta thấy cách phương thức Note gọi phương thức Document: Overriding the Read method for Note! Implementing the Write method dor Note! Thực thi giao diện tường minh Trong việc thực thi giao diện giờ, lớp thực thi (trong trường hợp Document) tạo phương thức thành viên ký hiệu kiểu trả phương thức mô tả giao diên Chúng ta không cần thiết khai báo tường minh thực thi giao diện, việc hiểu ngầm trình biên dịch Tuy nhiên, có vấn đề xảy lớp thực thi hai giao diện hai giao diện có phương thức ký hiệu Ví dụ 8.5 tạo hai giao diện: IStorable ITalk Sau thực thi phương thức Read() giao diện ITalk để đọc tiếng nội dung sách Không may phương thức tranh chấp với phương thức Read() IStorable mà Document phải thực thi Bởi hai phương thức IStorable ITalk có phương thức Read(),việc thực thi lớp Document phải sử dụng thực thi tường minh cho phương thức Với việc thực thi tường minh, lớp thực thi Document khai báo tường minh cho phương thức: void ITalk.Read(); Điều giải việc tranh chấp, tạo hàng loạt hiệu ứng thú vị Đầu tiên, không cần thiết sử dụng thực thi tường minh với phương thức khác Talk: public void Talk(); khơng có tranh chấp ta khai báo thông thường Điều quan trọng phương thức thực thi tường minh khơng có bổ sung truy cập: void ITalk.Read(); Phương thức hiểu ngầm public 197 Thực Thi Giao Diện Ngôn Ngữ Lập Trình C# Thật vậy, phương thức khai báo tường minh khơng khai báo với từ khóa bổ sung truy cập: abstract, virtual, override, new Một địều quan trọng khác truy cập phương thức thực thi tường minh thơng qua đối tượng Khi viết: theDoc.Read(); Trình biên dịch hiểu thực thi phương thức giao diện ngầm định cho IStorable Chỉ cách truy cập phương thức thực thi tường minh 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 áp dụng 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 { // 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() { 198 Thực Thi Giao Diện 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(); } } 199 Thực Thi Giao Diện 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ể phương thức giao diện Những người thiết kế lớp thu lợi giao diện thực thi thông qua thực thi tường minh không cho phép thành phần client lớp truy cập sử dụng thông qua việc gán cho giao diện Giả sử nghĩa đối tượng Document thực thi giao diện IStorable, khơng muốn phương thức Read() Write() phần giao diện public lớp Document Chúng ta sử dụng thực thi tường minh để chắn truy cập thông qua việc gán cho giao diện Điều cho phép lưu trữ ngữ nghĩa lớp Document thực thi giao diện IStorable Nếu thành phần client muốn đối tượng thực thi giao diện IStorable, thực gán tường minh cho giao diện để gọi phương thức thực thi giao diện Nhưng sử dụng đối tượng Document nghĩa khơng có phương thức Read() Write() Thật vậy, lựa chọn thể phương thức thông qua thực thi tường minh, trưng bày vài phương thức thực thi phần lớp Document số phương thức khác khơng Trong ví dụ 8.5, đối tượng Document trưng bày phương thức Talk() phương thức lớp Document, phương thức Talk.Read() thể thông qua gán cho giao diện Thậm chí IStorable khơng có phương thức Read(), chọn thực thi tường minh phương thức Read() để phương thức bên phương thức Document Chúng ta lưu ý thực thi giao diện tường minh ngăn ngừa việc sử dụng từ khóa virtual, lớp dẫn xuất hỗ trợ để thực thi lại phương thức Do đó, Note dẫn xuất từ Document, thực thi lại phương thức Talk.Read() lớp Document thực thi phương thức Talk.Read() ảo Ẩ n thành viên Ngôn ngữ C# cho phép ẩn thành viên giao diện Ví dụ, có giao diện IBase với thuộc tính P: interface IBase 200 Thực Thi Giao Diện ... diện cho phép đối xử giao diện cách đa hình Nói cách khác, tạo hai hay nhiều lớp thực thi giao diện, sau cách truy cập lớp thông qua giao diện Gán đối tượng cho giao diện Trong nhiều trường hợp,... giao diện có tên sau: IStorable, ICloneable, Danh sách sở danh sách giao diện mà giao diện mở rộng, phần trình bày phần thực thi nhiều giao diện chương Phần thân giao diện phần thực thi giao diện. .. Compress Mở rộng giao diện C# cung cấp chức cho mở rộng giao diện có cách thêm phương thức thành viên hay bổ sung cách làm việc cho thành viên Ví dụ, mở rộng giao diện ICompressible với giao diện ILoggedCompressible

Ngày đăng: 25/07/2014, 07:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan