Những chủ đề tiến bộ trong C# - Các ép kiểu do người dùng định nghĩa – Phần 2
lượt xem 13
download
Ép kiểu giữa lớp dẫn xuất và lớp cơ sở Để xem làm thế nào việc ép kiểu này làm, ta xem xét 2 lớp Mybase và Myderived , trong đó Mydrived được dẫn xuất trực tiếp hoặc gián tiếp từ lớp cơ sở đầu tiên từ lớp Myderived đến Mybase ; luôn luôn ( giả sử hàm dựng có giá trị)có thể viết : MyDerived derivedObject = new MyDerived(); MyBase baseCopy = derivedObject; Trong trường hợp này,chúng ta ép kiểu không tường minh từ myderived đến mybase. ...
Bình luận(0) Đăng nhập để gửi bình luận!
Nội dung Text: Những chủ đề tiến bộ trong C# - Các ép kiểu do người dùng định nghĩa – Phần 2
- Những chủ đề tiến bộ trong C# Các ép kiểu do người dùng định nghĩa – Phần 2 Ép kiểu giữa lớp dẫn xuất và lớp cơ sở Để xem làm thế nào việc ép kiểu này làm, ta xem xét 2 lớp Mybase và Myderived , trong đó Mydrived được dẫn xuất trực tiếp hoặc gián tiếp từ lớp cơ s ở đầu tiên từ lớp Myderived đến Mybase ; luôn luôn ( giả sử hàm dựng có giá trị)có thể viết : MyDerived derivedObject = new MyDerived(); MyBase baseCopy = derivedObject; Trong trường hợp này,chúng ta ép kiểu không tường minh từ myderived đến mybase. điều này làm việc bởi vì luật là bất kì tham chiếu đến 1 kiểu mybase được cho phép để chuyển thành đối tượng của lớp mybase hoặc đến đối tượng bất kì được dẫn xuất từ lớp mybase.trong ngôn ngữ lập trình hướng đối tượng, thể hiện của lớp dẫn xuất là thể hiện của một lớp cơ sở cộng thêm
- với một thứ gì đó thêm. tất cả chức năng và thuộc tính được định nghĩa trong lớp cơ sở cũng được định nghĩa trong lớp dẫn xuất . Bây giờ ta có thể viết MyBase derivedObject = new MyDerived(); MyBase baseObject = new MyBase(); MyDerived derivedCopy1 = (MyDerived) derivedObject; // OK MyDerived derivedCopy2 = (MyDerived) baseObject; Tất cả các câu lệnh trên là hợp lệ trong C# và minh họa việc ép kiểu từ lớp cơ sở sang lớp dẫn xuất. tuy nhiên câu lệnh cuối sẽ tung ra biệt lệ khi thực thi. Chú ý rằng những lệnh ép kiểu mà trình biên dịch cung cấp , mà chuyển giữa lớp cơ sở và lớp dẫn xuất thì không thực sự chuyển bất cứ dữ liệu nào trên các đối tượng.tất cả chúng làm là thiết lập một sự tham chiếu mới để quy cho một đối tượng nếu nó hợp lệ cho việc chuyển đổi .những lệnh ép kiểu này thì rất khác trong tự nhiên từ những cái mà ta thường xuyên tự định nghĩa.ví dụ, trong ví dụ Simplecurrency chúng ta định nghĩa việc ép kiểu là chuyển giữa 1 kiểu tiền tệ sang kiểu số thực. trong ép kiểu thực-thành- currency, chúng ta thực sự tạo một cấu trúc currency mới và khởi tạo nó với giá trị được yêu cầu .những lệnh ép kiểu tiền định nghĩa giữa những lớp cơ s
- ở và lớp dẫn xuất không làm điều này.nếu ta thực sự chuyển 1 thể hiện Mybase thành một đối tượng Myderived thực với giá trị dựa trên nội dung của thể hiện Mybase, ta sẽ không thể sử dụng cú pháp ép kiểu để làm điều này.tuỳ chọn hợp lí nhất là thường xuyên định nghĩa 1 hàm dựng của lớp dẫn xuất mà lấy thể hiện của lớp cơ sở như là 1 thông số và có hàm dựng này biểu diễn việc khởi tạo chính xác: class DerivedClass : BaseClass { public DerivedClass(BaseClass rhs) { // khởi tạo đối tượng từ thể hiện Base } // etc. Ép kiểu boxing và unboxing Ví dụ với cấu trúc currency: Currency balance = new Currency(40,0); object baseCopy = balance;
- Khi ép kiểu không tường minh trên được thực hiện nội dung của balance được sao chép vào heap trong đối tượng box và đối tượng basecopy tham khảo đến đối tượng này.khi chúng ta định nghĩa cấu trúc currency , .net framework cung cấp không tường minh một lớp ( ẩn) khác , một lớp currency boxed, mà chứa đựng tất cả các trường như là cấu trúc currency nhưng là kiểu tham chiếu lưu trong heap.điều này xảy ra bất cứ khi nào chúng ta định nghĩa một kiểu dữ liệu- dù đó là struct hay kiểu liệt kê ( enum) ,và kiểu tham khảo boxed tồn tại đáp ứng đến tất cả kiểu dữ liệu nguyên thuỷ int,double,uint, và ...ta không thể truy nhập vào các lớp này nhưng chúng sẽ làm việc bất cứ khi nào có việc ép kiểu thành đối tượng khi chúng ta ép kiểu currency thành đối tượng một thể hiện currency boxed tạo ra và khởi tạo với tất cả các giá trị từ cấu trúc currency. trong ví dụ trên basecopy sẽ tham khảo đến lớp currency boxed. Ép kiểu còn được biết đến như là unboxing,dùng cho việc ép kiểu giữa những lớp kiểu tham chiếu cơ sở và kiểu tham chiếu dẫn xuất.đó là ép kiểu tường minh, bởi vì 1 biệt lệ sẽ được tung ra nếu đối tượng được ép kiểu không ép đúng. object derivedObject = new Currency(40,0); object baseObject = new object(); Currency derivedCopy1 = (Currency)derivedObject; // OK
- Currency derivedCopy2 = (Currency)baseObject; // Exception thrown Khi sử dụng boxing và unboxing điều quan trọng để hiểu là cả hai tiến trình này thực sự sao chép dữ liệu vào một đối tượng boxed hay unboxed. chính vì lí do đó, thao tác trên đối tượng hộp sẽ không tácđộng đến nội dung của kiểu dữ liệu nguyên thuỷ. Multiple casting Ví dụ với cấu trúc currency , giả sử trình biên dịch chạm trán với các dòng mã sau: Currency balance = new Currency(10,50); long amount = (long)balance; double amountD = balance; Đầu tiên chúng ta khởi tạo 1 thể hiện currency ,sau đó ép nó thành kiểu long.vấn đề là ta chưa định nghĩa ép kiểu cho việc này. tuy nhiên đoạn mã nay vẫn biên dịch thành công bởi vì trình biên dịch nhận ra rằng ta đã định nghĩa ép kiểu không tường minh để chuyển currency thành float.và nó biết cách chuyển tường minh từ float sang long. ví lí do đó, nó sẽ biên dịch đầu tiên là chuyển balance sang float rồi từ float sang long.tương tự cho kiểu double tuy nhiên do chuyển tử float sang double không tường minh , do đó chúng ta có thể viết lại tường minh :
- Currency balance = new Currency(10,50); long amount = (long)(float)balance; double amountD = (double)(float)balance; Đoạn mã sau gây ra lỗi : Currency balance = new Currency(10,50); long amount = balance; Do việc chuyển từ float sang long cần tường minh. Nếu ta không cẩn thận khi nào ta sẽ định nghĩa ép kiểu, thì trình biên dịch có thể sẽ dẫn đến một kết quả không mong đợi..Ví dụ, giả sử một ai khác trong nhóm đang viết cấu trúc Currency,mà sẽ hửu ích nếu có khả năng chuyển 1 số uint chứa tổng số Cent thành 1 kiểu Currency ( Cent chứ không phải Dollar bởi vì nó sẽ không làm mất đi phần thập phân của Dollar ) .vì thế việc ép kiểu này có thể được viết như sau : public static implicit operator Currency (uint value) { return new Currency(value/100u, (ushort)(value%100)); } // Don't do this!
- Lưu ý chữ u sau số 100 đảm bảo rằng value/100u sẽ đuợc phiên dịch thành 1 số uint. nếu ta viết value/100 thì trình biên dịch sẽ phiên dịch sẽ phiên dịch nó là số int chứ không phải uint. Lý do ta không nên viết mã kiểu này là vì : tất cả ta làm trong đó là chuyển 1 uint chứa 350 thành 1 kiểu Currency và ngược trở lại. Ta sẽ có gì sau khi thi hành mã này : uint bal = 350; Currency balance = bal; uint bal2 = (uint)balance; Câu trả lời không phải là 350 mà là 3. Ta chuyển 350 thành 1 Currency 1 cách không tường minh, trả về kết quả Balance.Dollars=3 , Balance.Cents=50. Sau đó trình biên dịch tính toán hướng tốt nhất để chuyển trở lại.Balance được chuyển không tường minh thành 1 kiểu float ( giá trị 3.5) và số này được chuyển tường minh thành 1 số uint với giá trị 3 Vấn đề là có 1 sự xung đột giữa cách các ép kiểu của ta dịch các số nguyên integer.các ép kiểu giữa Currency và float dịch 1 số nguyên giá trị 1 thành 1 doolar, nhưng cách ép kiểu uint-to-Currency cuối nhất sẽ dịch giá trị này là 1 cent.Nếu ta muốn lớp của ta dễ dàng để dùng thì ta nên chắc rằng tất cả các ép kiểu của ta cư xử hợp với nhau,theo hướng cho ra cùng 1 kết quả. Trong
- trường hợp này ,giải pháp là viết lại hàm ép kiểu uint-to-Balance để nó phiên dịch 1 số nguyên integer giá trị 1 thành 1 dollar. public static implicit operator Currency (uint value) { return new Currency(value, 0); } 1 cách kiểm tra tốt là xét xem 1 chuyển đổi có cho ra cùng kết quả hay không.Lớp Currency đưa ra 1 ví dụ tốt cho kiểm tra này : Currency balance = new Currency(50, 35); ulong bal = (ulong) balance; Hiện tại chỉ có 1 cách mà trình biên dịch có thể thực hiện việc chuyển đổi này: bằng cách chuyển Currency thành 1 float 1 cách không tường minh.việc chuyển float thành ulong đòi hỏi 1 sự tường minh. Giả sử ta thêm 1 cách ép kiểu khác,để chuyển 1 cách không tường minh từ Currency thành uint.Ta sẽ làm điều này bằng việc cập nhật lại cấu trúc Currency bằng cách thêm vào các ép kiểu thành và từ kiểu uint. Ta xem đây là ví dụ SimpleCurrency2: public static implicit operator Currency (uint value) { return new Currency(value, 0);
- } public static implicit operator uint (Currency value) { return value.Dollars; } Bây giờ trình biên dịch có 1 cách khác để chuyển từ Currency thành ulong: là chuyển từ Currency thành uint 1 cách tường minh sau đó thành ulong 1 cách không tường minh. để kiểm tra ví dụ SimpleCurrency2, ta sẽ thêm đoạn mã này vào phần kiểm tra trong SimpleCurrency: try { Currency balance = new Currency(50,35); Console.WriteLine(balance); Console.WriteLine("balance is " + balance); Console.WriteLine("balance is (using ToString()) " + balance.ToString()); uint balance3 = (uint) balance; Console.WriteLine("Converting to uint gives " + balance3); Chạy ví dụ ta có kết quả SimpleCurrency2
- 50 balance is $50.35 balance is (using ToString()) $50.35 Converting to uint gives 50 After converting to float, = 50.35 After converting back to Currency, = $50.34 Now attempt to convert out of range value of -$100.00 to a Currency: Exception occurred: Arithmetic operation resulted in an overflow. Kết quả chỉ ra việc chuyển đổi thành uint thành công,ta đã mất phần cent của Currency trong việc chuyển này. Ép kểu 1 số float âm thành Currency đã gây ra 1 biệt lệ. Tuy nhiên kết quả cũng giải thích 1 vấn đề nữa mà ta cần nhận thức khi làm việc với ép kiểu.dòng đầu tiên của kết quả không trình bày balance đúng,trình bày 50 thay vì $50.35. Console.WriteLine(balance); Console.WriteLine("balance is " + balance); Console.WriteLine("balance is (using ToString()) " + balance.ToString()); Chỉ có 2 dòng cuối trình bày đúng Currency thành chuỗi.Vấn để ở đây là khi ta kết hợp ép kiểu với phương thức overload,ta lấy 1 nguồn khác không dự đoán trước được.Ta sẽ xem kĩ hơn vấn đề này sau.
- Câu lệnh thứ 3 Console.WriteLine() gọi tường minh phương thức Currency.ToString() đảm bảo Currency được trình bày là chuỗi.Cái thứ 2 không làm như vậy. tuy nhiên ,chuỗi " balance is" được truyền đến Console.WriteLine làm rõ hơn rằng thông số được phiên dịch như chuỗi. Chính vì vậy Currency,ToString() sẽ được gọi không tường minh. Phương thức Console.WriteLine đầu tiên đơn giản truyền1 cấu trúc Currency đến Console.WriteLine.Console.WriteLine có nhiều hàm overload,nhưng không có cái nào trong chúng lấy cấu trúc Currency .Vì vậy trình biên dịch sẽ bắt đầu tìm xem nó có thể ép Currency thành kiểu nào để làm cho nó phù hợp với overload của Console.WriteLine. khi nó xảy ra , một trong những overload Console.WriteLine() được thiết kế để trình bày uint 1 cách nhanh chóng và hiệu quả, và nó lấy 1 uint như là 1 thông số, và ta vừa cung cấp 1 ép kiểu chuyển Currency thành uint không tường minh.Kết quả là nó có thể trình bày. Quả thực Console.WriteLine có overload khác lấy 1 double làm thông số và trình bày giá trị của double.nếu ta xem kĩ kết quả từ ví dụ SimpleCurrency đầu ta sẽ sẽ thấy dòng kết quả đầu tiên sẽ trình bày Currency như là 1 số double.trong ví dụ đó không có việc ép kiểu trực tiếp từ Currency thàng uint ,vì vậy trình biên dịch sẽ lấy Currency-to-float-to-double để làm.
CÓ THỂ BẠN MUỐN DOWNLOAD
-
Những chủ đề tiến bộ trong C# - Events ( Các sự kiện)
16 p | 205 | 32
-
Những chủ đề tiến bộ trong C# - Delegate – Phần 1
14 p | 92 | 16
-
Những chủ đề tiến bộ trong C# - Các ép kiểu do người dùng định nghĩa – Phần 1
12 p | 137 | 15
-
Những chủ đề tiến bộ trong C# - Delegate – Phần 2
10 p | 103 | 15
-
Những chủ đề tiến bộ trong C# - Quản lý bộ nhớ bên dưới của C#
10 p | 156 | 14
-
Những chủ đề tiến bộ trong C# - Lỗi và xử lí biệt lệ - Phần 3
12 p | 124 | 13
-
Những chủ đề tiến bộ trong C# - Các chỉ thị tiền xử lí trong C#
6 p | 110 | 11
-
Những chủ đề tiến bộ trong C# Các mã không an toàn – Phần 1
11 p | 85 | 10
-
Những chủ đề tiến bộ trong C# - Attribute
7 p | 97 | 9
-
Những chủ đề tiến bộ trong C#
12 p | 83 | 9
-
Những chủ đề tiến bộ trong C# Lỗi và xử lí biệt lệ - Phần 2
17 p | 80 | 9
-
Mang giá lạnh đến desktop với bộ hình nền và giao diện “mùa đông”
6 p | 84 | 8
-
Những chủ đề tiến bộ trong C# - Lỗi và xử lí biệt lệ - Phần 1
15 p | 121 | 8
-
Những chủ đề tiến bộ trong C# Các mã không an toàn – Phần 3
6 p | 78 | 8
-
Gỡ bỏ Windows 8 khỏi hệ thống dualboot – P.1
5 p | 86 | 5
-
10 thuật ngữ dễ nhầm lẫn nhất trong lĩnh vực công nghệ thông tin
3 p | 95 | 5
-
Những tính năng bảo mật trong Windows 8.1
10 p | 75 | 4
Chịu trách nhiệm nội dung:
Nguyễn Công Hà - Giám đốc Công ty TNHH TÀI LIỆU TRỰC TUYẾN VI NA
LIÊN HỆ
Địa chỉ: P402, 54A Nơ Trang Long, Phường 14, Q.Bình Thạnh, TP.HCM
Hotline: 093 303 0098
Email: support@tailieu.vn