intTypePromotion=1
zunia.vn Tuyển sinh 2024 dành cho Gen-Z zunia.vn zunia.vn
ADSENSE

Những chủ đề tiến bộ trong C# - Các ép kiểu do người dùng định nghĩa – Phần 1

Chia sẻ: Nguyen Uyen | Ngày: | Loại File: PDF | Số trang:12

135
lượt xem
14
download
 
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Trong các chương trước ta được học về cách chuyển đổi giá trị giữa những kiểu dữ liệu cơ bản. chúng ta cũng đã học hai cách ép kiểu là : - Không tường minh (Implicit) - Tường minh (Explicit) Vì c# cho phép ta định nghĩa những lớp và cấu trúc riêng,do đó ta cũng muốn có những cách thức mà cho phép ta chuyển đổi giữa những loại dữ liệu của riêng ta.

Chủ đề:
Lưu

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 1

  1. Những chủ đề tiến bộ trong C# Các ép kiểu do người dùng định nghĩa – Phần 1 Trong các chương trước ta được học về cách chuyển đổi giá trị giữa những kiểu dữ liệu cơ bản. chúng ta cũng đã học hai cách ép kiểu là : - Không tường minh (Implicit) - Tường minh (Explicit) Vì c# cho phép ta định nghĩa những lớp và cấu trúc riêng,do đó ta cũng muốn có những cách thức mà cho phép ta chuyển đổi giữa những loại dữ liệu của riêng ta. C# cho phép làm điều đó.cơ chế của nó là ta có thể định nghĩa một ép kiểu như là một thao tác thành viên của một trong những lớp thích hợp. việc ép kiểu phải được đánh dấu là implicit hoặc explicit để chỉ định cách mà bạn muốn sử dụng với nó. cũng giống như việc ép kiểu cơ bản : nếu bạn biết việc ép kiểu là an toàn ,dù là bất cứ giá trị nào đựợc giữ bởi biến nguồn, thì bạn định nghĩa nó như là implicit.ngược lại nếu bạn biết việc
  2. ép kiểu có thể đi đến sự liều lĩnh - mất dữ liệu hay một biệt lệ sẽ bị tung ra - bạn nên định nghĩa ép kiểu như là explicit. Bạn nên định nghĩa bất kỳ kiểu ép kiểu mà bạn viết là tường minh nếu có bất kì giá trị dữ liệu nguồn nào mà việc ép kiểu có khả năng thất bại, hoặc nếu có sự mạo hiểm do một biệt lệ được tung ra. Cú pháp của việc định nghĩa ép kiểu cũng giống như việc overload thao tác . không phải ngẫu nhiên mà ta nói thế , bởi vì theo cách mà ép kiểu được xem như là thao tác là tác động của nó là chuyển từ kiểu dữ liệu nguồn sang kiểu dữ liệu đích. để minh hoạ cho cú pháp này, cú pháp sau được lấy từ ví dụ mà sẽ được giới thiệu sau đây trong phần này: public static implicit operator float (Currency value) { // xử lí } Đoạn mã này là một phần của cấu trúc - currency - được dùng để lưu trữ tiền.ép kiểu được định nghĩa ở đây cho phép chúng ta chuyển đổi 1 cách ẩn dụ giá trị của 1 kiểu tiền tệ sang 1 số thực ( float). chú ý rằng nếu việc chuyển được khai báo như là implicit, thì trình biên dịch cho phép nó sử
  3. dụng cả implicit và explicit. nếu nó được khai báo như là explicit , thì trình biên dịch chỉ cho phép nó sử dụng như là explicit. Trong khai báo này việc ép kiểu được khai báo là static. giống như các thao tác được overload , C# đòi hỏi việc ép kiểu là static. điều này có nghĩa là mỗi ép kiểu cũng lấy một thông số , mà là kiểu dữ liệu trong nguồn Thực hành ép kiểu dữ liệu do người sử dụng định nghĩa. Trong phần này, chúng ta sẽ xem xét việc ép kiểu implicit và explicit của kiểu dữ liệu này trong ví dụ Simplecurrency. trong ví dụ này chúng ta định nghĩa 1 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, nhưng bạn vẫn có thể viết riêng 1 cấu trúc struct hay một lớp để trình bày giá trị tiền nếu bạn muốn biểu diễn quy trình tài chính phức tạp và do đó muốn có một phương thức cụ thể để thực thi như là một lớp. cấu trúc của ép kiểu là giống nhau cho struct hay lớp . trong ví dụ này là struct, nhưng nó cũng làm việc tốt nếu bạn khai báo currency như là một lớp. khởi đầu , định nghĩa cấu trúc currency như sau: struct Currency { public uint Dollars;
  4. public ushort Cents; public Currency(uint dollars, ushort cents) { this.Dollars = dollars; this.Cents = cents; } Việc dùng kiểu dữ liệu không dấu cho trường Dollar và cent bảo đảm rằng một thể hiện của currency chỉ giữ 1 số dương.chúng ta giới hạn nó bằng cách này để có thể minh hoạ một số điểm về tường minh sau này.để giữ cho lớp đơn giản,ta chọn các trường là public, nhưng nói chung bạn sẽ phải định nghĩa chúng private, và định nghĩa những thuộc tính đáp ứng cho dollar và cent Chúng ta hãy bắt đầu bằng cách giả sử như là bạn muốn chuyển giá trị từ currency sang float, mà phần nguyên của kiểu float sẽ trình bày dollar: Currency balance = new Currency(10,50); float f = balance; // ta muốn f được đặt là 10.5 Để cho phép làm điều này , cần định nghĩa 1 ép kiểu. từ đây ta thêm vào trong cấu trúc currency:
  5. public static implicit operator float (Currency value) { return value.Dollars + (value.Cents/100.0f); } Ép kiểu này là implicit, Đây là sự chọn lựa dễ nhận thấy , bởi vì , nó nên rõ ràng từ định nghĩa trong currency, bất kì giá trị nào lưu trữ trong currency cũng có thể lưu trong kiểu float. Nếu chuyển ngược thì sao? từ một số float sang currency .trong trường hợp này việc chuyển đổi có thể không làm việc ,nếu float lưu trữ số âm,còn currency thì không , và số này sẽ lưu trữ phần làm tròn vào trong trường dollar của currency.nếu float chứa đựng một giá trị không thích hợp việc chuyển nó sẽ gây ra một kết quả không dự đoán truớc. do đó việc chuyển đổi này nên được khai báo là explicit. sau đây là đoạn mã thử đầu tiên , tuy nhiên nó không gửi kết quả 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);
  6. } Đoạn mã sau sẽ dịch đúng : float amount = 45.63f; Currency amount2 = (Currency)amount; Tuy nhiên đoạn mã sau sẽ báo lỗi bởi vì nó sử dụng một ép kiểu tường mình một cách không rõ ràng : float amount = 45.63f; Currency amount2 = amount; // sai Sau đây là phương thức main() mà khởi tạo một struct Currency, và thực hiện một vài việc chuyển đổi. vào đầu đoạn mã, chúng ta viết giá trị của biến balance theo 2 cách ( để minh họa 1 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);
  7. 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); } }
  8. Chú ý rằng ta đặt toàn bộ đoạn mã trong khối try để bắt bất cứ biệt lệ nào xảy ra trong quá trình ép kiểu. Sau khi chạy ta có kết quả 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 quả cho thấy đoạn mã không làm việc như mong đợi. ở phần đầu việc chuyển cho kết quả sai là 50.34 thay vì 50.35. trong phần hai, không có biệt lệ nào được sinh ra khi ta cố chuyển một giá trị nằm ngoài vùng. Lỗi dầu tiên là do làm tròn.. nếu ép kiểu từ float sang uint , máy tính sẽ cắt bỏ số hơn là làm tròn nó.máy tính lưu trữ số dạng nhị phân hơn là thập phân.và phần dư 0.35 không thể được trình bày một cách chính xác như là phần dư dạng nhị phân.do đó máy tính lưu trữ một số nhỏ hơn 0.35, mà có thể trình bày chính xác trong dạng nhị phân.nhân cho 100 và lấy phần dư nhỏ hơn 35 cắt thành 34 cent.rõ ràng trong hoà n cảnh này, lỗi cắt bỏ là
  9. nghiêm trọng.để tránh chúng thì chắc rằng một sự làm tròn thông minh phải được thi hành trong việc chuyển đổi số.thật may mắn Microsoft đã viết một lớp để làm điều đó : System.Convert . System.Convert chứa đựng một số lượng lớn những phương thức static biểu diễn việc chuyển đổi số, và Phương thức mà chúng ta muốn là System.convert.Uint6(). Bây giờ chúng ta sẽ kiểm tra xem tại sao biệt lệ tràn đã không xuất hiện . vấn đề ở đây là : nơi mà việc tràn xuất hiện không thực sự nằm trong hàm main()- nó ở bên trong mã của thao tác ép kiểu. mà được gọi từ phương thức main() . và chúng ta đã không kiểm tra đoạn mã đó. Giải pháp ở đây là cho phép kiểm tra ngay trong 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); }
  10. } Chú ý rằng 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 này để tính phần dollar vì việc cắt bỏ trong giá trị float đã đưa ra kết quả ta cần Ép kiểu giữa những lớp Ví dụ trên cho ta thấy việc ép kiểu giữa 2 kiểu dữ liệu đã được định nghĩa trước.tuy nhiên ta cũng có thể ép kiểu giữa 2 cấu trúc hoặc lớp mà ta định nghĩa. có 2 hạn chế cần quan tâm : - Ta không thể định nghĩa một ép kiểu nếu một trong những lớp đ ược dẫn xuất từ 1 lớp khác. - Ép kiểu phải được định nghĩa bên trong việc định kiểu dữ liệu nguồn hay đích. Để minh hoạ những yêu cầu này , giả sử rằng ta có biểu đồ lớp sau:
  11. Nói cách khác,lớp c và d được dẫn xuất gián tiếp từ lớp a.trong trường hợp này,chỉ có những ép kiểu riêng giữa a,b,c,d mà hợp pháp sẽ được chuyển là những lớp c và d bởi vì những những lớp này không được dẫn xuất từ mỗi lớp khác.mã có thể như sau : public static explicit operator D(C value) { // and so on } public static explicit operator C(D value) { // and so on } Cho mỗi kiểu ép kiểu này , ta có quyền chọn nơi mà ta đặt định nghĩa- bên trong lớp định nghĩa C hoặc bên trong lớp định nghĩa D, nhưng không nằm ở bất cứ chổ nào khác.C# đòi hỏi bạn đặt định nghĩa của 1 ép kiểu bên trong lớp ( hoặc cấu trúc)nguồn hoặc bên trong lớp ( hoặc cấu trúc) đích Mỗi lần bạn định nghĩa một ép kiểu bên trong 1 lớp , bạn không thể định nghĩa giống như vậy bên trong những lớp khác.rõ ràng, chỉ nên có 1 hàm ép
  12. kiểu cho mỗi chuyển đổi. nếu không trình biên dịch không biết sử dụng cái nào.
ADSENSE

CÓ THỂ BẠN MUỐN DOWNLOAD

 

Đồng bộ tài khoản
2=>2