mov dh,10
push dx
mov ah,6
mov al,0
mov bh,07
mov cx,0
mov dx,184f
int 10
pop dx
mov dl,0
mov cx,50
push cx
mov ah,2
mov bh,0
int 10
mov ah,9
mov al,20
mov bh,0
add bl,11
mov cx,1
int 10
inc dl
pop cx
loop 0117
mov ah,8
int 21
cmp al,1b
jz 144
inc dh
cmp dh,19
jnz 142
mov dh,0
jmp 102
int 20
Thứ Sáu, 29 tháng 11, 2013
Thứ Hai, 18 tháng 11, 2013
Nền tảng ngôn ngữ C # - Phần 3
Kiểu dữ liệu chuỗi khá thân thiện với người lập trình trong bất cứ ngôn ngữ lập trình nào, kiểu dữ liệu chuỗi lưu giữ một mảng những ký tự.
| |
Kiểu chuỗi ký tự
Để khai báo một chuỗi chúng ta sử dụng từ khoá string tương tự như cách tạo một thể hiện của bất cứ đối tượng nào:
string chuoi;
Một hằng chuỗi được tạo bằng cách đặt các chuỗi trong dấu nháy đôi:
"Xin chao"
Đây là cách chung để khởi tạo một chuỗi ký tự với giá trị hằng:
string chuoi = "Xin chao"
Kiểu chuỗi sẽ được đề cập sâu trong các bài sau.
Định danh
Định danh là tên mà người lập trình chỉ định cho các kiểu dữ liệu, các phương thức, biến, hằng, hay đối tượng.... Một định danh phải bắt đầu với một ký tự chữ cái hay dấu gạch dưới, các ký tự còn lại phải là ký tự chữ cái, chữ số, dấu gạch dưới.
Theo qui ước đặt tên của Microsoft thì đề nghị sử dụng cú pháp lạc đà (camel notation) bắt đầu bằng ký tự thường để đặt tên cho các biến là cú pháp Pascal (Pascal notation) với ký tự đầu tiên hoa cho cách đặt tên hàm và hầu hết các định danh còn lại. Hầu như Microsoft không còn dùng cú pháp Hungary như iSoNguyenhay dấu gạch dưới Bien_Nguyen để đặt các định danh.
Các định danh không được trùng với các từ khoá mà C# đưa ra, do đó chúng ta không thể tạo các biến có tên như class hay int được. Ngoài ra, C# cũng phân biệt các ký tự thường và ký tự hoa vì vậy C# xem hai biến bienNguyen và bienguyen là hoàn toàn khác nhau.
Biểu thức
Những câu lệnh mà thực hiện việc đánh giá một giá trị gọi là biểu thức. Một phép gán một giá trị cho một biến cũng là một biểu thức:
var1 = 24;
Trong câu lệnh trên phép đánh giá hay định lượng chính là phép gán có giá trị là 24 cho biến var1. Lưu ý là toán tử gán (’=’) không phải là toán tử so sánh. Do vậy khi sử dụng toán tử này thì biến bên trái sẽ nhận giá trị của phần bên phải. Các toán tử của ngôn ngữ C# như phép so sánh hay phép gán sẽ được trình bày chi tiết trong mục toán tử của phần này.
Do var1 = 24 là một biểu thức được định giá trị là 24 nên biểu thức này có thể được xem như phần bên phải của một biểu thức gán khác:
var2 = var1 = 24;
Lệnh này sẽ được thực hiện từ bên phải sang khi đó biến var1 sẽ nhận được giá trị là 24 và tiếp sau đó thìvar2 cũng được nhận giá trị là 24. Do vậy cả hai biến đều cùng nhận một giá trị là 24. Có thể dùng lệnh trên để khởi tạo nhiều biến có cùng một giá trị như:
a = b = c = d = 24;
Khoảng trắng (whitespace)
Trong ngôn ngữ C#, những khoảng trắng, khoảng tab và các dòng được xem như là khoảng trắng (whitespace), giống như tên gọi vì chỉ xuất hiện những khoảng trắng để đại diện cho các ký tự đó. C# sẽ bỏ qua tất cả các khoảng trắng đó, do vậy chúng ta có thể viết như sau:
Hay
var1 = 24;
var1 = 24 ;
và trình biên dịch C# sẽ xem hai câu lệnh trên là hoàn toàn giống nhau. Tuy nhiên lưu ý là khoảng trắng trong một chuỗi sẽ không được bỏ qua. Nếu chúng ta viết:
System.WriteLine("Xin chao!");
mỗi khoảng trắng ở giữa hai chữ "Xin" và "chao" đều được đối xử bình thường như các ký tự khác trong chuỗi.
Hầu hết việc sử dụng khoảng trắng như một sự tùy ý của người lập trình. Điều cốt yếu là việc sử dụng khoảng trắng sẽ làm cho chương trình dễ nhìn dễ đọc hơn Cũng như khi ta viết một văn bản trong MS Word nếu không trình bày tốt thì sẽ khó đọc và gây mất cảm tình cho người xem. Còn đối với trình biên dịch thì việc dùng hay không dùng khoảng trắng là không khác nhau.
Tuy nhiên, củng cần lưu ý khi sử dụng khoảng trắng như sau:
int x = 24;
tương tự như:
int x=24;
nhưng không giống như:
intx=24;
Trình biên dịch nhận biết được các khoảng trắng ở hai bên của phép gán là phụ và có thể bỏ qua, nhưng khoảng trắng giữa khai báo kiểu và tên biến thì không phải phụ hay thêm mà bắt buộc phải có tối thiểu một khoảng trắng. Điều này không có gì bất hợp lý, vì khoảng trắng cho phép trình biên dịch nhận biết được từ khoá int và không thể nào nhận được intx.
Tương tự như C/C++, trong C# câu lệnh được kết thúc với dấu chấm phẩy ’;’. Do vậy có thể một câu lệnh trên nhiều dòng, và một dòng có thể nhiều câu lệnh nhưng nhất thiết là hai câu lệnh phải cách nhau một dấu chấm phẩy.
Câu lệnh (statement)
Trong C# một chỉ dẫn lập trình đầy đủ được gọi là câu lệnh. Chương trình bao gồm nhiều câu lệnh tuần tự với nhau. Mỗi câu lệnh phải kết thúc với một dấu chấm phẩy, ví dụ như:
int x; // một câu lệnh
x = 32; // câu lệnh khác
int y =x; // đây cũng là một câu lệnh
Những câu lệnh này sẽ được xử lý theo thứ tự. Đầu tiên trình biên dịch bắt đầu ở vị trí đầu của danh sách các câu lệnh và lần lượt đi từng câu lệnh cho đến lệnh cuối cùng, tuy nhiên chỉ đúng cho trường hợp các câu lệnh tuần tự không phân nhánh.
Có hai loại câu lệnh phân nhánh trong C# là : phân nhánh không có điều kiện (unconditional branching statement) và phân nhánh có điều kiện (conditional branching statement).
Ngoài ra còn có các câu lệnh làm cho một số đoạn chương trình được thực hiện nhiều lần, các câu lệnh này được gọi là câu lệnh lặp hay vòng lặp. Bao gồm các lệnh lặp for, while, do, in, và each sẽ được đề cập tới trong mục tiếp theo. Sau đây chúng ta sẽ xem xét hai loại lệnh phân nhánh phổ biến nhất trong lập trình C#.
Phân nhánh không có điều kiện
Phân nhánh không có điều kiện có thể tạo ra bằng hai cách: gọi một hàm và dùng từ khoá phân nhánh không điều kiện.
Gọi hàm
Khi trình biên dịch xử lý đến tên của một hàm, thì sẽ ngưng thực hiện hàm hiện thời mà bắt đầu phân nhánh dể tạo một gọi hàm mới. Sau khi hàm vừa tạo thực hiện xong và trả về một giá trị thì trình biên dịch sẽ tiếp tục thực hiện dòng lệnh tiếp sau của hàm ban đầu. ví dụ 3.6 minh họa cho việc phân nhánh khi gọi hàm.
Ví dụ 3.6: Gọi một hàm.
Kết quả:
Luồng chương trình thực hiện bắt đầu từ hàm Main xử lý đến dòng lệnh Func(), lệnh Func() thường được gọi là một lời gọi hàm. Tại điểm này luồng chương trình sẽ rẽ nhánh để thực hiện hàm vừa gọi. Sau khi thực hiện xong hàm Func, thì chương trình quay lại hàm Main và thực hiện câu lệnh ngay sau câu lệnh gọi hàmFunc.
Từ khoá phân nhánh không điều kiện
Để thực hiện phân nhánh ta gọi một trong các từ khóa sau: goto, break, continue, return, statementthrow. Việc trình bày các từ khóa phân nhánh không điều kiện này sẽ được đề cập trong chương tiếp theo. Trong phần này chỉ đề cập chung không đi vào chi tiết.
Hết phần 3
|
Nền tảng ngôn ngữ C # - Phần 2
Một biến là một vùng lưu trữ với một kiểu dữ liệu. Trong ví dụ trước cả x, và y điều là biến. Biến có thể được gán giá trị và cũng có thể thay đổi giá trị khi thực hiện các lệnh trong chương trình.
| |
Biến và hằng
Để tạo một biến chúng ta phải khai báo kiểu của biến và gán cho biến một tên duy nhất. Biến có thể được khởi tạo giá trị ngay khi được khai báo, hay nó cũng có thể được gán một giá trị mới vào bất cứ lúc nào trong chương trình. Ví dụ 3.1 sau minh họa sử dụng biến.
Ví dụ 3.1: Khởi tạo và gán giá trị đến một biến.
Kết quả:
Ngay khi khai báo biến ta đã gán giá trị là 9 cho biến, khi xuất biến này thì biến có giá trị là 9. Thực hiện phép gán biến cho giá trị mới là 15 thì biến sẽ có giá trị là 15 và xuất kết quả là 15.
Gán giá trị xác định cho biến
C# đòi hỏi các biến phải được khởi tạo trước khi được sử dụng. Để kiểm tra luật này chúng ta thay đổi dòng lệnh khởi tạo biến bien1 trong ví dụ 3.1 như sau:
int bien1;
và giữ nguyên phần còn lại ta được ví dụ 3.2:
Ví dụ 3.2: Sử dụng một biến không khởi tạo.
Khi biên dịch đoạn chương trình trên thì trình biên dịch C# sẽ thông báo một lỗi sau:
...error CS0165: Use of unassigned local variable ’bien1’
Việc sử dụng biến khi chưa được khởi tạo là không hợp lệ trong C#. Ví dụ 3.2 trên không hợp lệ.
Tuy nhiên không nhất thiết lúc nào chúng ta cũng phải khởi tạo biến. Nhưng để dùng được thì bắt buộc phải gán cho chúng một giá trị trước khi có một lệnh nào tham chiếu đến biến đó. Điều này được gọi là gán giá trị xác định cho biến và C# bắt buộc phải thực hiện điều này.
Ví dụ 3.3 minh họa một chương trình đúng.
Ví dụ 3.3: Biến không được khi tạo nhưng sau đó được gán giá trị.
Hằng
Hằng cũng là một biến nhưng giá trị của hằng không thay đổi. Biến là công cụ rất mạnh,tuy nhiên khi làm việc với một giá trị được định nghĩa là không thay đổi, ta phải đảm bảo giátrị của nó không được thay đổi trong suốt chương trình. Ví dụ, khi lập một chương trình thí nghiệm hóa học liên quan đến nhiệt độ sôi, hay nhiệt độ đông của nước, chương trình cần khai báo hai biến là DoSoi và DoDong, nhưng không cho phép giá trị của hai biến này bị thay đổi hay bị gán. Để ngăn ngừa việc gán giá trị khác, ta phải sử dụng biến kiểu hằng.
Hằng được phân thành ba loại: giá trị hằng (literal), biểu tượng hằng (symbolic constants), kiểu liệu kê (enumerations).
Giá trị hằng: ta có một câu lệnh gán như sau:
x = 100;
Giá trị 100 là giá trị hằng. Giá trị của 100 luôn là 100. Ta không thể gán giá trị khác cho 100 được.
Biểu tượng hằng: gán một tên cho một giá trị hằng, để tạo một biểu tượng hằng dùng từ khóa const và cú pháp sau:
=
Một biểu tượng hằng phải được khởi tạo khi khai báo, và chỉ khởi tạo duy nhất một lần trong suốt chương trình và không được thay đổi. Ví dụ:
const int DoSoi = 100;
Trong khai báo trên, 32 là một hằng số và DoSoi là một biểu tượng hằng có kiểu nguyên. Ví dụ 3.4 minh họa việc sử dụng những biểu tượng hằng.
Vi dụ 3.4: Sử dụng biểu tượng hằng.
Kết quả:
Ví dụ 3.4 tạo ra hai biểu tượng hằng chứa giá trị nguyên: DoSoi và DoDong, theo qui tắc đặt tên hằng thì tên hằng thường được đặt theo cú pháp Pascal, nhưng điều này không đòi hỏi bởi ngôn ngữ nên ta có thể đặt tùy ý.
Việc dùng biểu thức hằng này sẽ làm cho chương trình được viết tăng thêm phần ý nghĩa cùng với sự dễ hiểu. Thật sự chúng ta có thể dùng hằng số là 0 và 100 thay thế cho hai biểu tượng hằng trên, nhưng khi đó chương trình không được dễ hiểu và không được tự nhiên lắm. Trình biên dịch không bao giờ chấp nhận một lệnh gán giá trị mới cho một biểu tượng hằng.
Ví dụ 3.4 trên có thể được viết lại như sau :
Khi đó trình biên dịch sẽ phát sinh một lỗi sau:
Kiểu liệt kê
Kiểu liệt kê đơn giản là tập hợp các tên hằng có giá trị không thay đổi (thường được gọi là danh sách liệt kê).
Trong ví dụ 3.4, có hai biểu tượng hằng có quan hệ với nhau:
const int DoDong = 0;
const int DoSoi = 100;
Do mục đích mở rộng ta mong muốn thêm một số hằng số khác vào danh sách trên, như các hằng sau:
const int DoNong = 60; const int DoAm = 40; const int DoNguoi = 20;
Các biểu tượng hằng trên điều có ý nghĩa quan hệ với nhau, cùng nói về nhiệt độ của nước, khi khai báo từng hằng trên có vẻ cồng kềnh và không được liên kết chặt chẽ cho lắm. Thay vào đó C# cung cấp kiểu liệt kê để giải quyết vấn đề trên:
Mỗi kiểu liệt kê có một kiểu dữ liệu cơ sở, kiểu dữ liệu có thể là bất cứ kiểu dữ liệu nguyên nào như int, short, long... tuy nhiên kiểu dữ lịêu của liệt kê không chấp nhận kiểu ký tự. Để khai báo một kiểu liệt kê ta thực hiện theo cú pháp sau:
[thuộc tính] [bổ sung] enum
Thành phần thuộc tính và bổ sung là tự chọn sẽ được trình bày trong phần sau của sách.
Trong phần này chúng ta sẽ tập trung vào phần còn lại của khai báo. Một kiểu liệt kê bắt đầu với từ khóa enum, tiếp sau là một định danh cho kiểu liệt kê:
enum NhietDoNuoc
Thành phần kiểu cơ sở chính là kiểu khai báo cho các mục trong kiểu liệt kê. Nếu bỏ qua thành phần này thì trình biên dịch sẽ gán giá trị mặc định là kiểu nguyên int, tuy nhiên chúng ta có thể sử dụng bất cứ kiểu nguyên nào như ushort hay long,..ngoại trừ kiểu ký tự. Đoạn ví dụ sau khai báo một kiểu liệt kê sử dụng kiểu cơ sở là số nguyên không dấu uint:
Lưu ý là khai báo một kiểu liệt kê phải kết thúc bằng một danh sách liệt kê, danh sách liệt kê này phải có các hằng được gán, và mỗi thành phần phải phân cách nhau dấu phẩy.
Ta viết lại ví dụ minh họa 3-4 như sau.
Ví dụ 3.5: Sử dụng kiểu liệt kê để đơn giản chương trình.
Kết quả:
Mỗi thành phần trong kiểu liệt kê tương ứng với một giá trị số, trong trường hợp này là một số nguyên. Nếu chúng ta không khởi tạo cho các thành phần này thì chúng sẽ nhận các giá trị tiếp theo với thành phần đầu tiên là 0.
Ta xem thử khai báo sau:
Khi đó giá trị của ThuNhat là 0, giá trị của ThuHai là 1, giá trị của ThuBa là 10 và giá trị của ThuTu là 11.
Kiểu liệt kê là một kiểu hình thức do đó bắt buộc phải thực hiện phép chuyển đổi tường minh với các kiêu giá trị nguyên:
int x = (int) ThuTu.ThuNhat;
Hết phần 2
|
Nền tảng ngôn ngữ C # - Phần 1
Trong chương trước chúng ta đã tìm hiểu một chương trình C# đơn giản nhất. Chương trình đó chưa đủ để diễn tả một chương trình viết bằng ngôn ngữ C#, có quá nhiều phần và chi tiết đã bỏ qua. Do vậy trong chương này chúng ta sẽ đi sâu vào tìm hiểu cấu trúc và cú pháp của ngôn ngữ C#.
| |
Chương này sẽ thảo luận về hệ thống kiểu dữ liệu, phân biệt giữa kiểu dữ liệu xây dựng sẵn (như int, bool, string…) với kiểu dữ liệu do người dùng định nghĩa (lớp hay cấu trúc do người lập trình tạo ra...). Một số cơ bản khác về lập trình như tạo và sử dụng biến dữ liệu hay hằng cũng được đề cập cùng với cấu trúc liệt kê, chuỗi, định danh, biểu thức và cậu lệnh.
Trong phần hai của chương hướng dẫn và minh họa việc sử dụng lệnh phân nhánh if, switch, while, do...while, for, và foreach. Và các toán tử như phép gán, phép toán logic, phép toán quan hệ, và toán học...
Như chúng ta đã biết C# là một ngôn ngữ hướng đối tượng rất mạnh, và công việc của người lập trình là kế thừa để tạo và khai thác các đối tượng. Do vậy để nắm vững và phát triển tốt người lập trình cần phải đi từ những bước đi dầu tiên tức là đi vào tìm hiểu những phần cơ bản và cốt lõi nhất của ngôn ngữ.
Kiểu dữ liệu
C# là ngôn ngữ lập trình mạnh về kiểu dữ liệu, một ngôn ngữ mạnh về kiểu dữ liệu là phải khai báo kiểu của mỗi đối tượng khi tạo (kiểu số nguyên, số thực, kiểu chuỗi, kiểu điều khiển...) và trình biên dịch sẽ giúp cho người lập trình không bị lỗi khi chỉ cho phép một loại kiểu dữ liệu có thể được gán cho các kiểu dữ liệu khác. Kiểu dữ liệu của một đối tượng là một tín hiệu để trình biên dịch nhận biết kích thước của một đối tượng (kiểu int có kích thước là 4 byte) và khả năng của nó (như một đối tượng button có thể vẽ, phản ứng khi nhấn,...).
Tương tự như C++ hay Java, C# chia thành hai tập hợp kiểu dữ liệu chính: Kiểu xây dựng sẵn (built- in) mà ngôn ngữ cung cấp cho người lập trình và kiểu được người dùng định nghĩa (user-defined) do người lập trình tạo ra.
C# phân tập hợp kiểu dữ liệu này thành hai loại: Kiểu dữ liệu giá trị (value) và kiểu dữ liệu tham chiếu (reference). Việc phân chi này do sự khác nhau khi lưu kiểu dữ liệu giá trị và kiểu dữ liệu tham chiếu trong bộ nhớ. Đối với một kiểu dữ liệu giá trị thì sẽ được lưu giữ kích thước thật trong bộ nhớ đã cấp phát là stack. Trong khi đó thì địa chỉ của kiểu dữ liệu tham chiếu thì được lưu trong stack nhưng đối tượng thật sự thì lưu trong bộ nhớ heap.
Nếu chúng ta có một đối tượng có kích thước rất lớn thì việc lưu giữ chúng trên bộ nhớ heap rất có ích, trong chương 4 sẽ trình bày những lợi ích và bất lợi khi làm việc với kiểu dữ liệu tham chiếu, còn trong chương này chỉ tập trung kiểu dữ kiểu cơ bản hay kiểu xây dựng sẵn.
Ghi chú: Tất cả các kiểu dữ liệu xây dựng sẵn là kiểu dữ liệu giá trị ngoại trừ các đối tượng và chuỗi. Và tất cả các kiểu do người dùng định nghĩa ngoại trừ kiểu cấu trúc đều là kiểu dữ liệu tham chiếu.
Ngoài ra C# cũng hỗ trợ một kiểu con trỏ C++, nhưng hiếm khi được sử dụng, và chỉ khi nào làm việc với những đoạn mã lệnh không được quản lý (unmanaged code). Mã lệnh không được quản lý là các lệnh được viết bên ngoài nền .MS.NET, như là các đối tượng COM.
Kiểu dữ liệu xây dựng sẵn
Ngôn ngữ C# đưa ra các kiểu dữ liệu xây dựng sẵn rất hữu dụng, phù hợp với một ngôn ngữ lập trình hiện đại, mỗi kiểu dữ liệu được ánh xạ đến một kiểu dữ liệu được hỗ trợ bởi hệ thống xác nhận ngôn ngữ chung (Common Language Specification: CLS) trong MS.NET. Việc ánh xạ các kiểu dữ liệu nguyên thuỷ của C# đến các kiểu dữ liệu của .NET sẽ đảm bảo các đối tượng được tạo ra trong C# có thể được sử dụng đồng thời với các đối tượng được tạo bởi bất cứ ngôn ngữ khác được biên dịch bởi .NET, như VB.NET.
Mỗi kiểu dữ liệu có một sự xác nhận và kích thước không thay đổi, không giống như C++, int trong C# luôn có kích thước là 4 byte bởi vì nó được ánh xạ từ kiểu Int32 trong . NET.
Bảng 3.1 sau sẽ mô tả một số các kiểu dữ liệu được xây dựng sẵn
Bảng 3.1 : Mô tả các kiểu dữ liệu xây dựng sẵn.
Ghi chú: Kiểu giá trị logic chỉ có thể nhận được giá trị là true hay false mà thôi. Một giá trị nguyên không thể gán vào một biến kiểu logic trong C# và không có bất cứ chuyển đổi ngầm định nào. Điều này khác với C/C++, cho phép biến logic được gán giá trị nguyên, khi đó giá trị nguyên 0 là false và các giá trị còn lại làtrue.
Chọn kiểu dữ liệu
Thông thường để chọn một kiểu dữ liệu nguyên để sử dụng như short, int hay long thường dựa vào độ lớn của giá trị muốn sử dụng. Ví dụ, một biến ushort có thể lưu giữ giá trịtừ 0 đến 65.535, trong khi biến ulong có thể lưu giữ giá trị từ 0 đến 4.294.967.295, do đó tùy vào miền giá trị của phạm vi sử dụng biến mà chọn các kiểu dữ liệu thích hợp nhất. Kiểu dữ liệu int thường được sử dụng nhiều nhất trong lập trình vì với kích thước 4 byte của nó cũng đủ để lưu các giá trị nguyên cần thiết.Kiểu số nguyên có dấu thường được lựa chọn sử dụng nhiều nhất trong kiểu số trừ khi có lý do chính đáng để sử dụng kiểu dữ liệu không dấu.
Stack là một cấu trúc dữ liệu lưu trữ thông tin dạng xếp chồng tức là vào sau ra trước (Last In First Out : LIFO), điều này giống như chúng ta có một chồng các đĩa, ta cứ xếp các đĩa vào chồng và khi lấy ra thì đĩa nào nằm trên cùng sẽ được lập ra trước, tức là đĩa vào sau sẽ được lấy ra trước.
Trong C#, kiểu giá trị như kiểu số nguyên được cấp phát trên stack, đây là vùng nhớ được thiết lập để lưu các giá trị, và vùng nhớ này được tham chiếu bởi tên của biến.
Kiểu tham chiếu như các đối tượng thì được cấp phát trên heap. Khi một đối tượng được cấp phát trên heap thì địa chỉ của nó được trả về, và địa chỉ này được gán đến một tham chiếu.
Thỉnh thoảng cơ chế thu gom sẽ hũy đối tượng trong stack sau khi một vùng trong stack được đánh dấu là kết thúc. Thông thường một vùng trong stack được định nghĩa bởi một hàm. Do đó, nếu chúng ta khai báo một biến cục bộ trong một hàm là một đối tượng thì đối tượng này sẽ đánh dấu để hũy khi kết thúc hàm. Những đối tượng trên heap sẽ được thu gom sau khi một tham chiếu cuối cùng đến đối tượng đó được gọi.
Cách tốt nhất khi sử dụng biến không dấu là giá trị của biến luôn luôn dương, biến này thường thể hiện một thuộc tính nào đó có miền giá trị dương. Ví dụ khi cần khai báo một biến lưu giữ tuổi của một người thì ta dùng kiểu byte (số nguyên từ 0-255) vì tuổi của người không thể nào âm được. Kiểu float, double vàdecimal đưa ra nhiều mức độ khác nhau về kích thước cũng như độ chính xác.Với thao tác trên các phân số nhỏ thì kiểu float là thích hợp nhất. Tuy nhiên lưu ý rằng trình biên dịch luôn luôn hiểu bất cứ một số thực nào cũng là một số kiểu double trừ khi chúng ta khai báo rõ ràng. Để gán một số kiểu float thì số phải có ký tự f theo sau.
float soFloat = 24f;
Kiểu dữ liệu ký tự thể hiện các ký tự Unicode, bao gồm các ký tự đơn giản, ký tự theo mã Unicode và các ký tự thoát khác được bao trong những dấu nháy đơn. Ví dụ, A là một ký tự đơn giản trong khi \u0041 là một ký tự Unicode. Ký tự thoát là những ký tự đặc biệt bao gồm hai ký tự liên tiếp trong đó ký tự dầu tiên là dấu chéo ’\’. Ví dụ, \t là dấu tab. Bảng 3.2 trình bày các ký tự đặc biệt.
Bảng 3.2 : Các kiểu ký tự đặc biệt.
Chuyển đổi các kiểu dữ liệu
Những đối tượng của một kiểu dữ liệu này có thể được chuyển sang những đối tượng của một kiểu dữ liệu khác thông qua cơ chế chuyển đổi tường minh hay ngầm định. Chuyển đổi nhầm định được thực hiện một cách tự động, trình biên dịch sẽ thực hiện công việc này. Còn chuyển đổi tường minh diễn ra khi chúng ta gán ép một giá trị cho kiểu dữ liệu khác.
Việc chuyển đổi giá trị ngầm định được thực hiện một cách tự động và đảm bảo là không mất thông tin. Ví dụ, chúng ta có thể gán ngầm định một số kiểu short (2 byte) vào một số kiểu int (4 byte) một cách ngầm định. Sau khi gán hoàn toàn không mất dữ liệu vì bất cứ giá trị nào của short cũng thuộc về int:
short x = 10;
int y = x; // chuyển đổi ngầm định
Tuy nhiên, nếu chúng ta thực hiện chuyển đổi ngược lại, chắc chắn chúng ta sẽ bị mất thông tin. Nếu giá trị của số nguyên đó lớn hơn 32.767 thì nó sẽ bị cắt khi chuyển đổi. Trình biên dịch sẽ không thực hiện việc chuyển đổi ngầm định từ số kiểu int sang số kiểu short:
short x;
int y = 100;
x = y; // Không biên dịch, lỗi !!!
Để không bị lỗi chúng ta phải dùng lệnh gán tường minh, đoạn mã trên được viết lại như sau:
short x;
int y = 500;
x = (short) y; // Ép kiểu tường minh, trình biên dịch không báo lỗi
Hết phần 1
|
Thứ Sáu, 15 tháng 11, 2013
Thực thi giao diện – Phần 2
C# cung cấp chức năng cho chúng ta mở rộng một giao diện đã có bằng cách thêm các phương thức và các thành viên hay bổ sung cách làm việc cho các thành viên. Ví dụ, chúng ta có thể mở rộng giao diện Icompressible với một giao diện mới là IloggedCompressible. | |
Mở rộng giao diện Giao diện mới này mở rộng giao diện cũ bằng cách thêm phương thức ghi log các dữ liệu đã lưu: Các lớp khác có thể thực thi tự do giao diện Icompressible hay IloggedCompressible tùy thuộc vào mục đích có cần thêm chức năng hay không. Nếu một lớp thực thi giao diện IloggedCompressible, thì lớp này phải thực thi tất cả các phương thức của cả hai giao diện Icompressible và giao diệnIloggedCompressible. Những đối tượng của lớp thực thi giao diện IloggedCompressible có thể được gán cho cả hai giao diện IloggedCompressible và Icompressible. Kết hợp các giao diện Một cách tương tự, chúng ta có thể tạo giao diện mới bằng cách kết hợp các giao diện cũ và ta có thể thêm các phương thức hay các thuộc tính cho giao diện mới. Ví dụ, chúng ta quyết định tạo một giao diệnIstorableCompressible. Giao diện mới này sẽ kết hợp những phương thức của cả hai giao diện và cũng thêm vào một phương thức mới để lưu trữ kích thước nguyên thuỷ của các dữ liệu trước khi nén: Ví dụ 8.2: Minh họa việc mở rộng và kết hợp các giao diện. Kết quả: Ví dụ 8.2 bắt đầu bằng việc thực thi giao diện Istorable và giao diện Icompressible. Sau đó là phần mở rộng đến giao diện IloggedCompressible rồi sau đó kết hợp cả hai vào giao diện IstorableCompressible. Và giao diện cuối cùng trong ví dụ là Iencrypt. Chương trình Tester tạo đối tượng Document mới và sau đó gán lần lượt vào các giao diện khác nhau. Khi một đối tượng được gán cho giao diện IloggedCompressible, chúng ta có thể dùng giao diện này để gọi các phương thức của giao diện Icompressible bởi vì ILogged- Compressible mở rộng và thừa kế các phương thức từ giao diện cơ sở: Tuy nhiên, ở đây chúng ta không thể gọi phương thức Read() bởi vì phương thức này của giao diệnIstorable, không liên quan đến giao diện này. Nếu chúng ta thêm lệnh này vào thì chương trình sẽ biên dịch lỗi. Nếu chúng ta gán vào giao diện IstorableCompressible, do giao diện này kết hợp hai giao diệnIstorable và giao diện Icompressible, chúng ta có thể gọi tất cả những phương thức củaIstorableCompressible, Icompressible, và Istorable: Chúng ta có thể truy cập những thành viên của giao diện Istorable như thể là các thành viên của lớpDocument: hay là ta có thể tạo thể hiện của giao diện bằng cách gán đối tượng Document cho một kiểu dữ liệu giao diện, và sau đó sử dụng giao diện này để truy cập các phương thức: Ghi chú: Cũng như đã nói trước đây, chúng ta không thể tạo thể hiện của giao diện một cách trực tiếp.Do đó chúng ta không thể thực hiện như sau: Tuy nhiên chúng ta có thể tạo thể hiện của lớp thực thi như sau: Sau đó chúng ta có thể tạo thể hiện của giao diện bằng cách gán đối tượng thực thi đến kiểu dữ liệu giao diện, trong trường hợp này là Istorable: Chúng ta có thể kết hợp những bước trên như sau: Nói chung, cách thiết kế tốt nhất là quyết định truy cập những phương thức của giao diện thông qua tham chiếu của giao diện. Do vậy cách tốt nhất là sử dụng isDoc.Read(), hơn là sử dụng doc.Read() trong ví dụ trước. Truy cập thông qua giao diện cho phép chúng ta đối xử giao diện một cách đa hình. Nói cách khác, chúng ta tạo hai hay nhiều hơn những lớp thực thi giao diện, và sau đó bằng cách truy cập lớp này chỉ thông qua giao diện. Hết phần 2 |
Thực thi giao diện – Phần 1
Giao diện là ràng buộc, giao ước đảm bảo cho các lớp hay các cấu trúc sẽ thực hiện một điều gì đó. Khi một lớp thực thi một giao diện, thì lớp này báo cho các thành phần client biết rằng lớp này có hỗ trợ các phương thức, thuộc tính, sự kiện và các chỉ mục khai báo trong giao diện. | |
Một giao diện đưa ra một sự thay thế cho các lớp trừu tượng để tạo ra các sự ràng buộc giữa những lớp và các thành phần client của nó. Những ràng buộc này được khai báo bằng cách sử dụng từ khóa interface, từ khóa này khai báo một kiểu dữ liệu tham chiếu để đóng gói các ràng buộc. Một giao diện thì giống như một lớp chỉ chứa các phương thức trừu tượng. Một lớp trừu tượng được dùng làm lớp cơ sở cho một họ các lớp dẫn xuất từ nó. Trong khi giao diện là sự trộn lẫn với các cây kế thừa khác. Khi một lớp thực thi một giao diện, lớp này phải thực thi tất cả các phương thức của giao diện. Đây là một bắt buộc mà các lớp phải thực hiện. Trong bài này chúng ta sẽ thảo luận cách tạo, thực thi và sử dụng các giao diện. Ngoài ra chúng ta cũng sẽ bàn tới cách thực thi nhiều giao diện cùng với cách kết hợp và mở rộng giao diện. Và cuối cùng là các minh họa dùng để kiểm tra khi một lớp thực thi một giao diện. Thực thi một giao diệnCú pháp để định nghĩa một giao diện như sau: Phần thuộc tính chúng ta sẽ đề cập sau. Thành phần bổ sung truy cập bao gồm: public, private,protected, internal, và protected internal đã được nói đến trong các bài trước, ý nghĩa tương tự như các bổ sung truy cập của lớp. Theo sau từ khóa interface là tên của giao diện. Thông thường tên của giao diện được bắt đầu với từ I hoa (điều này không bắt buộc nhưng việc đặt tên như vậy rất rõ ràng và dễ hiểu, tránh nhầm lẫn với các thành phần khác). Ví dụ một số giao diện có tên như sau: Danh sách cơ sở là danh sách các giao diện mà giao diện này mở rộng, phần này sẽ được trình bày trong phần thực thi nhiều giao diện của chương. Phần thân của giao diện chính là phần thực thi giao diện sẽ được trình bày bên dưới. Giả sử chúng ta muốn tạo một giao diện nhằm mô tả những phương thức và thuộc tính của một lớp cần thiết để lưu trữ và truy cập từ một cơ sở dữ liệu hay các thành phần lưu trữ dữ liệu khác như là một tập tin. Chúng ta quyết định gọi giao diện này là Istorage. Trong giao diện này chúng ta xác nhận hai phương thức: Read() và Write(), khai báo này sẽ được xuất hiện trong phần thân của giao diện như sau: Mục đích của một giao diện là để định nghĩa những khả năng mà chúng ta muốn có trong một lớp. Ví dụ, chúng ta có thể tạo một lớp tên là Document, lớp này lưu trữ các dữ liệu trong cơ sở dữ liệu, do đó chúng ta quyết định lớp này này thực thi giao diện Istorable. Để làm được điều này, chúng ta sử dụng cú pháp giống như việc tạo một lớp mới Document được thừa kế từ IStorable bằng dùng dấu hai chấm (:) và theo sau là tên giao diện: Bây giờ trách nhiệm của chúng ta, với vai trò là người xây dựng lớp Document phải cung cấp một thực thi có ý nghĩa thực sự cho những phương thức của giao diện Istorable. Chúng ta phải thực thi tất cả các phương thức của giao diện, nếu không trình biên dịch sẽ báo một lỗi. Sau đây là đoạn chương trình minh họa việc xây dựng lớp Document thực thi giao diện Istorable. Ví dụ 8.1: Sử dụng một giao diện. Kết quả: Ví dụ 8.1 định nghĩa một giao diện Istorable với hai phương thức Read(), Write() và một thuộc tính tên là Status có kiểu là số nguyên.. Lưu ý rằng trong phần khai báo thuộc tính không có phần thực thi cho get()và set() mà chỉ đơn giản là khai báo có hành vi là get() và set(): Ngoài ra phần định nghĩa các phương thức của giao diện không có phần bổ sung truy cập (ví dụ như:public, protected, internal, private). Việc cung cấp các bổ sung truy cập sẽ tạo ra một lỗi. Những phương thức của giao diện được ngầm định là public vì giao diện là những ràng buộc được sử dụng bởi những lớp khác. Chúng ta không thể tạo một thể hiện của giao diện, thay vào đó chúng ta sẽ tạo thể hiện của lớp có thực thi giao diện. Một lớp thực thi giao diện phải đáp ứng đầy đủ và chính xác các ràng buộc đã khai báo trong giao diện. Lớp Document phải cung cấp cả hai phương thức Read() và Write() cùng với thuộc tính Status. Tuy nhiên cách thực hiện những yêu cầu này hoàn toàn phụ thuộc vào lớp Document. Mặc dù Istorage chỉ ra rằng lớpDocument phải có một thuộc tính là Status nhưng nó không biết hay cũng không quan tâm đến việc lớpDocument lưu trữ trạng thái thật sự của các biến thành viên, hay việc tìm kiếm trong cơ sở dữ liệu. Những chi tiết này phụ thuộc vào phần thực thi của lớp. Thực thi nhiều giao diện Trong ngôn ngữ C# cho phép chúng ta thực thi nhiều hơn một giao diện. Ví dụ, nếu lớp Document có thể được lưu trữ và dữ liệu cũng được nén. Chúng ta có thể chọn thực thi cả hai giao diện Istorable vàIcompressible. Như vậy chúng ta phải thay đổi phần khai báo trong danh sách cơ sở để chỉ ra rằng cả hai giao diện điều được thực thi, sử dụng dấu phẩy (,) để phân cách giữa hai giao diện: Do đó Document cũng phải thực thi những phương thức được xác nhận trong giao diện Icompressible: Bổ sung thêm phần khai báo giao diện Icompressible và định nghĩa các phương thức của giao diện bên trong lớp Document. Sau khi tạo thể hiện lớp Document và gọi các phương thức từ giao diện ta có kết quả tương tự như sau: Hết phần 1 |
Đăng ký:
Nhận xét (Atom)
