Các Chủ Đề Tiến Bộ Trong C# part 2

11 442 3
Các Chủ Đề Tiến Bộ Trong C# part 2

Đ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

Các ép kiểu người dùng định nghĩa Trong chương trước ta học cách chuyển đổi giá trị kiểu liệu học hai cách ép kiểu : - Khơng tường minh (Implicit) - Tường minh (Explicit) Vì c# cho phép ta định nghĩa lớp cấu trúc riêng,do ta muốn có cách thức mà cho phép ta chuyển đổi loại liệu riêng ta C# cho phép làm điều đó.cơ chế ta định nghĩa ép kiểu thao tác thành viên lớp thích hợp việc ép kiểu phải đánh dấu implicit explicit để định cách mà bạn muốn sử dụng với giống việc ép kiểu : bạn biết việc ép kiểu an toàn ,dù giá trị đựợc giữ biến nguồn, bạn định nghĩa implicit.ngược lại bạn biết việc ép kiểu đến liều lĩnh - liệu hay biệt lệ bị tung - bạn nên định nghĩa ép kiểu explicit Bạn nên định nghĩa kiểu ép kiểu mà bạn viết tường minh có giá trị liệu nguồn mà việc ép kiểu có khả thất bại, có mạo hiểm biệt lệ tung Cú pháp việc định nghĩa ép kiểu giống việc overload thao tác ngẫu nhiên mà ta nói , theo cách mà ép kiểu xem thao tác tác động chuyển từ kiểu liệu nguồn sang kiểu liệu đích để minh hoạ cho cú pháp này, cú pháp sau lấy từ ví dụ mà giới thiệu sau phần này: public static implicit operator float (Currency value) { // xử lí } Đoạn mã phần cấu trúc - currency - dùng để lưu trữ tiền.ép kiểu định nghĩa cho phép chuyển đổi cách ẩn dụ giá trị kiểu tiền tệ sang số thực ( float) ý việc chuyển khai báo implicit, trình biên dịch cho phép sử dụng implicit explicit khai báo explicit , trình biên dịch cho phép sử dụng explicit Trong khai báo việc ép kiểu khai báo static giống thao tác overload , C# đòi hỏi việc ép kiểu static điều có nghĩa ép kiểu lấy thông số , mà kiểu liệu nguồn Thực hành ép kiểu liệu người sử dụng định nghĩa Trong phần này, xem xét việc ép kiểu implicit explicit kiểu liệu ví dụ Simplecurrency ví dụ định nghĩa cấu trúc struct, currency, mà giữ tiền USA thông thường, C# cung cấp kiểu thập phân ( decimal) cho mục đích này, bạn viết riêng cấu trúc struct hay lớp để trình bày giá trị tiền bạn muốn biểu diễn quy trình tài phức tạp muốn có phương thức cụ thể để thực thi lớp cấu trúc ép kiểu giống cho struct hay lớp ví dụ struct, làm việc tốt bạn khai báo currency lớp khởi đầu , định nghĩa cấu trúc currency sau: struct Currency { public uint Dollars; public ushort Cents; public Currency(uint dollars, ushort cents) { this.Dollars = dollars; this.Cents = cents; } Việc dùng kiểu liệu không dấu cho trường Dollar cent bảo đảm thể currency giữ số dương.chúng ta giới hạn cách để minh hoạ số điểm tường minh sau này.để giữ cho lớp đơn giản,ta chọn trường public, nói chung bạn phải định nghĩa chúng private, định nghĩa thuộc tính đáp ứng cho dollar cent Chúng ta bắt đầu cách giả sử bạn muốn chuyển giá trị từ currency sang float, mà phần nguyên kiểu float trình bày dollar: Currency balance = new Currency(10,50); float f = balance; // ta muốn f đặt 10.5 Để cho phép làm điều , cần định nghĩa ép kiểu từ ta thêm vào cấu trúc currency: public static implicit operator float (Currency value) { return value.Dollars + (value.Cents/100.0f); } Ép kiểu implicit, Đây chọn lựa dễ nhận thấy , , nên rõ ràng từ định nghĩa currency, giá trị lưu trữ currency lưu kiểu float Nếu chuyển ngược sao? từ số float sang currency trường hợp việc chuyển đổi khơng làm việc ,nếu float lưu trữ số âm,cịn currency khơng , số lưu trữ phần làm tròn vào trường dollar currency.nếu float chứa đựng giá trị khơng thích hợp việc chuyển gây kết khơng dự đốn truớc việc chuyển đổi nên khai báo explicit sau đoạn mã thử , nhiên khơng gửi kết hoàn toàn đúng: public static explicit operator Currency (float value) { uint dollars = (uint)value; ushort cents = (ushort)((value-dollars)*100); return new Currency(dollars, cents); } Đoạn mã sau dịch : float amount = 45.63f; Currency amount2 = (Currency)amount; Tuy nhiên đoạn mã sau báo lỗi sử dụng ép kiểu tường cách không rõ ràng : float amount = 45.63f; Currency amount2 = amount; // sai Sau phương thức main() mà khởi tạo struct Currency, thực vài việc chuyển đổi vào đầu đoạn mã, viết giá trị biến balance theo cách ( để minh họa số điều cho phần sau) static void Main() { try { Currency balance = new Currency(50,35); Console.WriteLine(balance); Console.WriteLine("balance is " + balance); Console.WriteLine("balance is (using ToString()) " + balance.ToString()); float balance2= balance; Console.WriteLine("After converting to float, = " + balance2); balance = (Currency) balance2; Console.WriteLine("After converting back to Currency, = " + balance); Console.WriteLine("Now attempt to convert out of range value of " + "-$100.00 to a Currency:"); checked { balance = (Currency) (-50.5); Console.WriteLine("Result is " + balance.ToString()); } } catch(Exception e) { Console.WriteLine("Exception occurred: " + e.Message); } } Chú ý ta đặt toàn đoạn mã khối try để bắt biệt lệ xảy trình ép kiểu Sau chạy ta có kết sau : SimpleCurrency 50.35 Balance is $50.35 Balance is (using ToString()) $50.35 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: Result is $4294967246.60486 Kết cho thấy đoạn mã không làm việc mong đợi phần đầu việc chuyển cho kết sai 50.34 thay 50.35 phần hai, khơng có biệt lệ sinh ta cố chuyển giá trị nằm vùng Lỗi dầu tiên làm tròn ép kiểu từ float sang uint , máy tính cắt bỏ số làm trịn nó.máy tính lưu trữ số dạng nhị phân thập phân.và phần dư 0.35 trình bày cách xác phần dư dạng nhị phân.do máy tính lưu trữ số nhỏ 0.35, mà trình bày xác dạng nhị phân.nhân cho 100 lấy phần dư nhỏ 35 cắt thành 34 cent.rõ ràng hoàn cảnh này, lỗi cắt bỏ nghiêm trọng.để tránh chúng làm trịn thơng minh phải thi hành việc chuyển đổi số.thật may mắn Microsoft viết lớp để làm điều : System.Convert System.Convert chứa đựng số lượng lớn phương thức static biểu diễn việc chuyển đổi số, Phương thức mà muốn System.convert.Uint6() Bây kiểm tra xem biệt lệ tràn không xuất vấn đề : nơi mà việc tràn xuất không thực nằm hàm main()- bên mã thao tác ép kiểu mà gọi từ phương thức main() không kiểm tra đoạn mã Giải pháp cho phép kiểm tra hàm ép kiểu public static explicit operator Currency (float value) { checked { uint dollars = (uint)value; ushort cents = Convert.ToUInt16((value-dollars)*100); return new Currency(dollars, cents); } } Chú ý ta sử dụng convert.uint16() để tính phần xu thay cho đoạn mã trên.ta không cần dùng cách để tính phần dollar việc cắt bỏ giá trị float đưa kết ta cần Ép kiểu lớp Ví dụ cho ta thấy việc ép kiểu kiểu liệu định nghĩa trước.tuy nhiên ta ép kiểu cấu trúc lớp mà ta định nghĩa có hạn chế cần quan tâm : - Ta định nghĩa ép kiểu lớp dẫn xuất từ lớp khác - Ép kiểu phải định nghĩa bên việc định kiểu liệu nguồn hay đích Để minh hoạ yêu cầu , giả sử ta có biểu đồ lớp sau: Nói cách khác,lớp c d dẫn xuất gián tiếp từ lớp a.trong trường hợp này,chỉ có ép kiểu riêng a,b,c,d mà hợp pháp chuyển lớp c d những lớp khơng dẫn xuất từ lớp khác.mã sau : public static explicit operator D(C value) { // and so on } public static explicit operator C(D value) { // and so on } Cho kiểu ép kiểu , ta có quyền chọn nơi mà ta đặt định nghĩa- bên lớp định nghĩa C bên lớp định nghĩa D, không nằm chổ khác.C# đòi hỏi bạn đặt định nghĩa ép kiểu bên lớp ( cấu trúc)nguồn bên lớp ( cấu trúc) đích Mỗi lần bạn định nghĩa ép kiểu bên lớp , bạn định nghĩa giống bên lớp khác.rõ ràng, nên có hàm ép kiểu cho chuyển đổi khơng trình biên dịch sử dụng Ép kiểu lớp dẫn xuất lớp sở Để xem làm việc ép kiểu làm, ta xem xét lớp Mybase Myderived , Mydrived dẫn xuất trực tiếp gián tiếp từ lớp sở từ lớp Myderived đến Mybase ; ln ln ( 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 làm việc luật tham chiếu đến kiểu mybase cho phép để chuyển thành đối tượng lớp mybase đến đối tượng dẫn xuất từ lớp mybase.trong ngơn ngữ lập trình hướng đối tượng, thể lớp dẫn xuất thể lớp sở cộng thêm với thứ thêm tất chức thuộc tính định nghĩa lớp sở định nghĩa lớp dẫn xuất Bây ta viết MyBase derivedObject = new MyDerived(); MyBase baseObject = new MyBase(); MyDerived derivedCopy1 = (MyDerived) derivedObject; // OK MyDerived derivedCopy2 = (MyDerived) baseObject; Tất câu lệnh hợp lệ C# minh họa việc ép kiểu từ lớp sở sang lớp dẫn xuất nhiên câu lệnh cuối tung biệt lệ thực thi Chú ý lệnh ép kiểu mà trình biên dịch cung cấp , mà chuyển lớp sở lớp dẫn xuất khơng thực chuyển liệu đối tượng.tất chúng làm thiết lập tham chiếu để quy cho đối tượng hợp lệ cho việc chuyển đổi lệnh ép kiểu khác tự nhiên từ mà ta thường xuyên tự định nghĩa.ví dụ, ví dụ Simplecurrency định nghĩa việc ép kiểu chuyển kiểu tiền tệ sang kiểu số thực ép kiểu thực-thànhcurrency, thực tạo cấu trúc currency khởi tạo với giá trị yêu cầu lệnh ép kiểu tiền định nghĩa lớp s lớp dẫn xuất không làm điều này.nếu ta thực chuyển thể Mybase thành đối tượng Myderived thực với giá trị dựa nội dung thể Mybase, ta sử dụng cú pháp ép kiểu để làm điều này.tuỳ chọn hợp lí thường xuyên định nghĩa hàm dựng lớp dẫn xuất mà lấy thể lớp sở thơng số có hàm dựng biểu diễn việc khởi tạo xác: class DerivedClass : BaseClass { public DerivedClass(BaseClass rhs) { // khởi tạo đối tượng từ thể Base } // etc Ép kiểu boxing 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 thực nội dung balance chép vào heap đối tượng box đối tượng basecopy tham khảo đến đối tượng này.khi định nghĩa cấu trúc currency , net framework cung cấp không tường minh lớp ( ẩn) khác , lớp currency boxed, mà chứa đựng tất trường cấu trúc currency kiểu tham chiếu lưu heap.điều xảy định nghĩa kiểu liệu- dù struct hay kiểu liệt kê ( enum) ,và kiểu tham khảo boxed tồn đáp ứng đến tất kiểu liệu nguyên thuỷ int,double,uint, ta truy nhập vào lớp chúng làm việc có việc ép kiểu thành đối tượng ép kiểu currency thành đối tượng thể currency boxed tạo khởi tạo với tất giá trị từ cấu trúc currency ví dụ basecopy tham khảo đến lớp currency boxed Ép kiểu biết đến unboxing,dùng cho việc ép kiểu lớp kiểu tham chiếu sở kiểu tham chiếu dẫn xuất.đó ép kiểu tường minh, biệt lệ tung đối tượng ép kiểu không ép 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 unboxing điều quan trọng để hiểu hai tiến trình thực chép liệu vào đối tượng boxed hay unboxed lí đó, thao tác đối tượng hộp khơng tácđộng đến nội dung kiểu 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 dịng mã sau: Currency balance = new Currency(10,50); long amount = (long)balance; double amountD = balance; Đầu tiên khởi tạo thể currency ,sau ép thành kiểu long.vấn đề ta chưa định nghĩa ép kiểu cho việc nhiên đoạn mã biên dịch thành cơng trình biên dịch nhận ta định nghĩa ép kiểu không tường minh để chuyển currency thành float.và biết cách chuyển tường minh từ float sang long ví lí đó, biên dịch chuyển balance sang float từ float sang long.tương tự cho kiểu double nhiên chuyển tử float sang double không tường minh , 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 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 ta định nghĩa ép kiểu, trình biên dịch dẫn đến kết khơng mong đợi Ví dụ, giả sử khác nhóm viết cấu trúc Currency,mà hửu ích có khả chuyển số uint chứa tổng số Cent thành kiểu Currency ( Cent khơng phải Dollar không làm phần thập phân Dollar ) việc ép kiểu viết sau : public static implicit operator Currency (uint value) { return new Currency(value/100u, (ushort)(value%100)); } // Don't this! Lưu ý chữ u sau số 100 đảm bảo value/100u đuợc phiên dịch thành số uint ta viết value/100 trình biên dịch phiên dịch phiên dịch số int khơng phải uint Lý ta không nên viết mã kiểu : tất ta làm chuyển uint chứa 350 thành kiểu Currency ngược trở lại Ta có sau thi hành mã : uint bal = 350; Currency balance = bal; uint bal2 = (uint)balance; Câu trả lời 350 mà Ta chuyển 350 thành Currency cách không tường minh, trả kết Balance.Dollars=3 , Balance.Cents=50 Sau trình biên dịch tính tốn hướng tốt để chuyển trở lại.Balance chuyển không tường minh thành kiểu float ( giá trị 3.5) số chuyển tường minh thành số uint với giá trị Vấn đề có xung đột cách ép kiểu ta dịch số nguyên integer.các ép kiểu Currency float dịch số nguyên giá trị thành doolar, cách ép kiểu uint-to-Currency cuối dịch giá trị cent.Nếu ta muốn lớp ta dễ dàng để dùng ta nên tất ép kiểu ta cư xử hợp với nhau,theo hướng cho kết Trong trường hợp ,giải pháp viết lại hàm ép kiểu uint-to-Balance để phiên dịch số nguyên integer giá trị thành dollar public static implicit operator Currency (uint value) { return new Currency(value, 0); } cách kiểm tra tốt xét xem chuyển đổi có cho kết hay khơng.Lớp Currency đưa ví dụ tốt cho kiểm tra : Currency balance = new Currency(50, 35); ulong bal = (ulong) balance; Hiện có cách mà trình biên dịch thực việc chuyển đổi này: cách chuyển Currency thành float cách khơng tường minh.việc chuyển float thành ulong địi hỏi tường minh Giả sử ta thêm cách ép kiểu khác,để chuyển cách không tường minh từ Currency thành uint.Ta làm điều việc cập nhật lại cấu trúc Currency cách thêm vào ép kiểu thành từ kiểu uint Ta xem 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 trình biên dịch có cách khác để chuyển từ Currency thành ulong: chuyển từ Currency thành uint cách tường minh sau thành ulong cách khơng tường minh để kiểm tra ví dụ SimpleCurrency2, ta thêm đoạn mã vào phần kiểm tra 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 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 việc chuyển đổi thành uint thành công,ta phần cent Currency việc chuyển Ép kểu số float âm thành Currency gây biệt lệ Tuy nhiên kết giải thích vấn đề mà ta cần nhận thức làm việc với ép kiểu.dịng kết khơng trình bày balance đúng,trình bày 50 thay $50.35 Console.WriteLine(balance); Console.WriteLine("balance is " + balance); Console.WriteLine("balance is (using ToString()) " + balance.ToString()); Chỉ có dịng cuối trình bày Currency thành chuỗi.Vấn để ta kết hợp ép kiểu với phương thức overload,ta lấy nguồn khác khơng dự đốn trước được.Ta xem kĩ vấn đề sau Câu lệnh thứ Console.WriteLine() gọi tường minh phương thức Currency.ToString() đảm bảo Currency trình bày chuỗi.Cái thứ không làm nhiên ,chuỗi " balance is" truyền đến Console.WriteLine làm rõ thông số phiên dịch chuỗi Chính Currency,ToString() gọi khơng tường minh Phương thức Console.WriteLine đơ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ó chúng lấy cấu trúc Currency Vì trình biên dịch bắt đầu tìm xem ép Currency thành kiểu để làm cho phù hợp với overload Console.WriteLine xảy , overload Console.WriteLine() thiết kế để trình bày uint cách nhanh chóng hiệu quả, lấy uint thơng số, ta vừa cung cấp ép kiểu chuyển Currency thành uint khơng tường minh.Kết trình bày Quả thực Console.WriteLine có overload khác lấy double làm thơng số trình bày giá trị double.nếu ta xem kĩ kết từ ví dụ SimpleCurrency đầu ta sẽ thấy dòng kết trình bày Currency số double.trong ví dụ khơng có việc ép kiểu trực tiếp từ Currency thàng uint ,vì trình biên dịch lấy Currency-to-float-todouble để làm Code for Download: SimpleCurrency SimpleCurrency2 ... ToString()) " + balance.ToString()); float balance2= balance; Console.WriteLine("After converting to float, = " + balance2); balance = (Currency) balance2; Console.WriteLine("After converting back... amount = 45.63f; Currency amount2 = (Currency)amount; Tuy nhiên đoạn mã sau báo lỗi sử dụng ép kiểu tường cách khơng rõ ràng : float amount = 45.63f; Currency amount2 = amount; // sai Sau phương... Currency, = $50.34 Now attempt to convert out of range value of -$100.00 to a Currency: Result is $ 429 496 724 6.60486 Kết cho thấy đoạn mã không làm việc mong đợi phần đầu việc chuyển cho kết sai 50.34

Ngày đăng: 20/10/2013, 16:15

Từ khóa liên quan

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

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

Tài liệu liên quan