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 chia 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.
Kiểu dữ liệu (data
type) là một tập hợp gồm các nhóm dữ
liệu có cùng đặc tính, cách lưu trữ của dữ liệu và các phép toán xử lý trên trường dữ liệu đó;
nhằm mục đích phân loại các dữ liệu. Việc tìm hiểu và sử dụng đúng các kiểu dữ liệu sẽ giúp tối ưu hóa được vùng nhớ cũng như sẽ sử dụng được các hàm xử lý dữ liệu dành riêng cho mỗi kiểu dữ liệu.
@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 tảng .NET,
như là các đối
tượng COM.
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.
Các biến thuộc kiểu này có thể được
gán giá trị một cách trực tiếp,
đây là kiểu dữ liệu được ngôn ngữ
cung cấp. Nó kế thừa từ
class System.ValueType.
Một biến khi
khai báo kiểu dữ liệu thì hệ thống sẽ cấp phát bộ nhớ, giá trị
thì vùng nhớ của biến đó sẽ
chứa giá trị của dữ liệu.
Các bảng dưới
đây liệt kê danh
sách các kiểu dữ liệu có sẵn trong
C#:
Bảng 1.3. Kiểu số nguyên
Kiểu dữ liệu
|
Kích thước (bytes)
|
Ý nghĩa
|
byte
|
1
|
Số nguyên dương không dấu
có giá trị
từ 0 đến 255
|
sbyte
|
1
|
Số nguyên có dấu có giá trị
từ -128 đến 127
|
short
|
2
|
Số nguyên có dấu có giá trị
từ -32,768 đến 32,767
|
ushort
|
2
|
Số nguyên không dấu có giá trị
từ 0 đến 65,535
|
int
|
4
|
Số nguyên có dấu có giá trị
từ -2,147,483,647 đến
2,147,483,647
|
uint
|
4
|
Số nguyên không dấu có giá trị
từ 0 đến 4,294,967,295
|
long
|
8
|
Số nguyên có dấu có giá trị
từ -9,223,370,036,854,775,808 đến
9,223,370,036,854,775,807
|
ulong
|
8
|
Số nguyên không dấu có giá trị
từ 0 đến
18,446,744,073,709,551,615
|
Bảng 1.4. Kiểu ký tự
Kiểu dữ liệu
|
Kích thước (bytes)
|
Ý nghĩa
|
char
|
2
|
Chứa
một ký tự Unicode
|
Bảng 1.5. Kiểu logic
Kiểu dữ liệu
|
Kích thước (bytes)
|
Ý nghĩa
|
bool
|
1
|
Chứa
1 trong 2 giá trị logic là true hoặc false
|
@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.
Bảng 1.6. Kiểu số thực
Kiểu dữ liệu
|
Kích thước (bytes)
|
Ý nghĩa
|
float
|
4
|
Kiểu
số thực dấu chấm động có giá trị dao
động
từ
3.4E – 38 đến 3.4E + 38, với 7 chữ số có nghĩa
|
double
|
8
|
Kiểu
số thực dấu chấm động có giá trị dao
động
từ
1.7E – 308 đến 1.7E + 308, với 15, 16 chữ số có
nghĩa
|
decimal
|
8
|
Có
độ chính xác đến 28 con số và giá trị thập
phân, được dùng trong tính toán tài chính
|
Trong trường hợp quên
giá trị của từng loại kiểu dữ liệu, có thể sử dụng biểu thức sizeof(type) để
lấy kích cở chính xác của một biểu thức hoặc một biến nào đó. Giá trị trả về của sizeof() là kích cỡ của đối tượng
hoặc kiểu bằng giá trị byte.
Nếu biến khai
báo kiểu dữ liệu tham chiếu
thì vùng nhớ của nó không chứa dữ liệu thực sự
được lưu
trong một biến mà vùng nhớ của nó chỉ chứa địa chỉ của đối
tượng dữ liệu. Hay chúng tham chiếu đến vị trí nào đó trong bộ nhớ tùy theo địa chỉ đã
được lưu.
Giả sử dữ liệu trong
vùng nhớ thay đổi bởi một biến nào đó, thì các dữ liệu của biến khác cũng thay đổi tự động giá
trị của mình.
Ta có một số kiểu dữ liệu thuộc kiểu tham chiếu
có sẵn trong
C# như: object, dynamic, string... Và tất cả các kiểu dữ liệu do người dùng định
nghĩa đều là kiểu dữ liệu tham chiếu.
Đây là kiểu dữ liệu cơ bản nhất của tất cả các kiểu dữ liệu trong
.NET. Mọi kiểu dữ liệu đều được kế thừa từ System.Object nên kiểu object có thể gán mọi giá trị của các kiểu dữ liệu khác
như kiểu tham chiếu, kiểu giá trị hoặc kiểu do người
dùng tự định nghĩa,... Tuy nhiên trước khi gán giá trị nó cần được chuyển kiểu.
Ví dụ:
object obj;
obj = 123;
// chúng ta đã
chuyển kiểu dữ liệu object sang kiểu giá trị.
|
Đây là một kiểu dữ liệu mới
được
đưa vào trong C# 4.0. Ta có lưu mọi kiểu giá
trị trong biến kiểu dynamic. Đối
tượng thuộc kiểu này sẽ không
xác định
được kiểu dữ liệu cho
đến khi
chương trình được thực thi.
Ta khai báo kiểu dynamic như khai báo biến bình thường:
Cú pháp
dynamic <tên biến>;
//hoặc
dynamic <tên biến> =
<giá trị>;
|
Đây là một kiểu dữ liệu cho phép gán chuỗi vào biến. Nó
được kế thừa từ kiểu object, có hai cách gán giá trị sau:
string str = "Lap trinh
C#!";
//hoặc có thể
dùng cách dưới đây, với cách này thì có thể dữ
nguyên chuỗi truyền vào
//kể cả chuỗi có
nhiều dòng.
string str = @"Lap
trinh C#
voi
moi nguoi!";
|
Biến kiểu con trỏ lưu địa chỉ bộ nhớ của kiểu khác.
Con trỏ trong C# có cùng
khả năng như
con trỏ trong C hoặc C++. Cú pháp để khai báo biến kiểu
con trỏ là: type* identifier.
Ví dụ
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.
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 1.7. Các ký tự đặc biệt
Ký tự
|
Ý nghĩa
|
\’
|
Dấu
nháy đơn
|
\”
|
Dấu
nháy kép
|
\\
|
Dấu
chéo
|
\0
|
Ký
tự null
|
\a
|
Alert
|
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
Ngoài ra còn có các kiểu dữ liệu tham chiếu do người dùng tự
định nghĩa
như class, interface hoặc delegate sẽ tìm hiểu sau.
- Không gán giá trị cho biến (trừ kiểu string). Ví dụ: int a;
- Gán giá trị nằm ngoài kiểu dữ liệu của biến. Ví dụ: int b = 6.9;
- Dùng dấu nháy đơn cho kiểu string. Ví dụ: string d = 'Q'; Vẫn phải sử dụng dấu nháy kép mặc dù giá trị gán chỉ có 1 ký tự.
- Gán giá trị null cho biến không phải kiểu string. Ví dụ: long x = null; Ta có thể sửa thành long? x = null;
- Gán giá trị của biến có kiểu dữ liệu lớn hơn cho biến có kiểu dữ liệu nhỏ hơn. Ví dụ: int x = 10; byte y = x;
Mặc dù 10 vẫn nằm trong khoảng giá trị của kiểu byte nhưng vẫn không thể gán giá trị biến kiểu int cho biến kiểu byte được.