Chương 14: Toán tử
Tác giả: Sưu tầm
Khái quát
Cú pháp biểu thức C# được dựa trên cú pháp biểu thức C++.
Độ ưu tiên toán tử
Khi một biểu thức chứa nhiều toán tử, độ ưu tiên của các toán tử sẽ điều khiển thứ tự các thành phần của biểu thức được thực hiện. Độ ưu tiên mặc định có thể được thay đổi bằng cách nhóm các thành phần với các dấu ngoặc.
int value = 1 + 2 * 3; // 1 + (2 * 3) = 7
value = (1 + 2) * 3; // (1 + 2) * 3 = 9
Trong C#, tất cả các toán tử nhị phân có tính kết hợp trái, điều đó có nghĩa là các phép toán được thực hiện từ trái sang phải, ngoại trừ toán tử gán và toán tử điều khiện (?:), được thực hiện từ phải sang trái.
Bảng sau tóm tắt tất cả các toán tử theo độ ưu tiên từ cao đến thấp.
LOẠI |
TOÁN TỬ |
Phép cơ sở
|
(x) x.y f(x) a[x] x++ x— new typeof sizeof checked unchecked |
Phép toán một ngôi |
+ – ! ~ ++x –x (T)x |
Phép nhân |
* / % |
Phép cộng |
+ – |
Phép dịch bit |
<< >> |
Phép toán quan hệ |
< > <= >= is |
So sánh bằng |
== != |
Logic AND |
& |
Logic XOR |
^ |
Logic OR |
| |
Điều kiện AND |
&& |
Điều kiện OR |
|| |
Điều kiện |
?: |
Phép gán |
= *= /= %= += -= <<= >>= &= ^= |= |
Các toán tử dựng sẵn
Đối với các toán tử số học trong C#, có các toán tử dựng sẵn cho các kiểu int, uint, long, ulong, float, double, và decimal. Bởi vì không có các toán tử dựng sẵn cho các kiểu khác, nên các biểu thức trước tiên phải được chuyển sang một trong các kiểu mà nó có một toán tử trước khi được thực hiện. Điều này có nghĩa là khi các phép toán được thực hiện với các kiểu số mà nó có thể được chuyển đổi kiểu mặc nhiên thành int – những kiểu này là nhỏ hơn int – kết quả sẽ phải được chuyển đổi để chứa nó trong cùng kiểu.
// error
class Test {
public static void Main() {
short s1 = 15;
short s2 = 16;
short ssum = (short) (s1 + s2); // cast is required
int i1 = 15;
int i2 = 16;
int isum = i1 + i2; // no cast required
}
}
Các toán tử người sử dụng định nghĩa
Các toán tử người sử dụng định nghĩa có thể được khai báo cho các lớp, và chúng hoạt động trong cùng cách ứng xử mà trong đó các toán tử dựng sẵn hoạt động. Hãy xem Chương 25, “Chồng toán tử”, để biết thêm chi tiết.
Sự thăng cấp số học
Hãy xem Chương 15, “Chuyển đổi kiểu”, để biết về quy tắc của sự thăng cấp số học.
Các toán tử số học
Phần sau đây tóm tắt các toán tử số học có thể được thực hiện trong C#. Các kiểu dấu chấm động có các luật rất cụ thể mà chúng tuân theo; để có đầy đủ chi tiết, hãy xem CLR. Nếu được thực thi trong một ngữ cảnh checked, thì các biểu thức số học với các kiểu không phải là dấu chấm động có thể ném ra các ngoại lệ.
Phép cộng một ngôi (+)
Đối với phép cộng một ngôi kết quả đơn giản là giá trị của một toán hạng.
Phép trừ một ngôi (-)
Phép trừ một ngôi chỉ thực hiện trên các kiểu có một kiểu phủ định hợp lệ, và nó sẽ trả ra giá trị của một toán tử được trừ bởi 0.
Phép cộng (+)
Trong C#, kí hiệu + được sử dụng cho cả phép cộng và ghép chuỗi.
Phép cộng số học
Hai toán hạng được cộng lại với nhau. Nếu biểu thức được tính với một ngữ cảnh checked và tổng vượt quá giới hạn của biểu thực kết quả, một OverflowException được ném ra. Điều này được minh họa bằng đoạn mã sau:
using System;
class Test {
public static void Main() {
byte val1 = 200;
byte val2 = 201;
byte sum = (byte) (val1 + val2); // no exception
checked {
byte sum2 = (byte) (val1 + val2); // exception
}
}
}
Ghép chuỗi
Ghép chuỗi có thể được thực hiện giữa hai chuỗi, hay giữ một chuỗi và một toán hạng có kiểu object. Nếu toán hạng này hoặc toán hạng kia là null, một chuỗi rỗng được thay thế cho toán tử đó. Các toán hạng không có kiểu string sẽ tự động được chuyển đổi kiểu thành một chuỗi bằng cách gọi hàm ảo ToString() trong đối tượng.
Phép trừ (-)
Toán hạng thứ hai được trừ đi cho toán hạng thứ nhất. Nếu biểu thức được thực thi với một ngữ cảnh checked và hiệu là vượt quá phạm vi của kiểu kết quả, một OverflowException được ném ra.
Phép nhân (*)
Hai toán hạng được nhân lại với nhau. Nếu biểu thức được tính với ngữ cảnh checked, và kết quả vượt quá giới hạn của kiểu kết quả, một OverflowException được ném ra.
Phép chia (/)
Toán hạng thứ nhất chia cho toán hạng thứ hai. Nếu toán hạng thứ hai là 0, một ngoại lệ DivideByZero được ném ra.
Phép lấy phần dư (%)
Kết quả của x % y được tính như x – (x / y) * y bằng cách sử dụng các phép tính với số nguyên. Nếu y là 0, một ngoại lệ DivideByZero được ném ra.
Phép dịch bit (<< và >>)
Đối với phép dịch trái, các bit cao bị vứt bỏ và các vị trí bit trống ở phần thấp được đặt là 0.
Đối với phép dịch phải với uint hay ulong, các bit thấp được vứt bỏ và các vị trí bit trống ở phần cao được đặt là 0.
Đối với phép dịch phải với int hay long, các bit thấp được vứt bỏ, và các bit cao trống được thay bằng 0 nếu x không âm, và 1 nếu x âm.
Phép tăng và phép giảm (++ và –)
Toán tử tăng sẽ tăng giá trị của một biến lên 1, và toán tử giảm sẽ giảm giá trị của một biến xuống 1.
Phép tăng và phép giảm có thể hoặc sử dụng như một toán tử tiền tố, mà các biến sẽ được sửa đổi trước khi được đọc, hoặc như toán tử hậu tố, mà giá trị được trả ra trước khi được sửa đổi.
Ví dụ:
int k = 5;
int value = k++; // value is 5
value = –k; // value is still 5
value = ++k; // value is 6
Các toán tử quan hệ và toán tử logic
Các toán tử quan hệ được sử dụng để so sánh hai giá trị, và toán tử logic được sử dụng để thực hiện các thao tác bit trên các giá trị.
Phép phủ định logic (!)
Toán tử ! được sử dụng để trả ra giá trị phủ định của một giá trị kiểu Boolean.
Các toán tử quan hệ
C# định nghĩa các toán tử quan hệ sau:
PHÉP TOÁN |
MÔ TẢ |
a == b |
trả ra true nếu a bằng b |
a != b |
trả ra true nếu a không bằng b |
a < b |
trả ra true nếu a nhỏ hơn b |
a <= b |
trả ra true nếu a nhỏ hơn hoặc bằng b |
a > b |
trả ra true nếu a lớn hơn b |
a >= b |
trả ra true nếu a lớn hơn hoặc bằng b |
Các toán tử này trả ra kết quả kiểu bool.
Khi thực hiện một phép so sánh giữa hai đối tượng kiểu tham chiếu, trình biên dịch trước tiên sẽ tìm các toán tử quan hệ được định nghĩa trên đối tượng. Nếu nó không tìm thấy toán tử thích hợp, và quan hệ là == hoặc là !=, thì một toán tử quan hệ thích hợp sẽ được gọi từ lớp đối tượng. Toán tử này so sánh xem hai toán hạng là cùng đối tượng, chứ không phải xem chúng có cùng giá trị.
Đối với kiểu giá trị, tiến trình là tương tự, ngoại trừ một toán tử quan hệ dựng sẵn cho các kiểu giá trị so sánh mỗi trường trong một cấu trúc, và trả ra true nếu tất cả các giá trị đồng nhất.
Đối với kiểu string, các toán tử quan hệ được chồng nên == và != so sánh các giá trị chuỗi, chứ không phải tham chiếu.
Các toán tử logic
C# định nghĩa các toán tử logic sau:
TOÁN TỬ |
MÔ TẢ |
& |
AND cấp bit của hai toán hạng |
| |
OR cấp bit của hai toán hạng |
^ |
XOR cấp bit của hai toán hạng |
&& |
AND logic của hai toán hạng |
|| |
OR logic của hai toán hạng |
Các toán tử &, | và ^ thường được sử dụng trên các kiểu dữ liệu số nguyên, mặc dù chúng cũng có thể được áp dụng cho kiểu bool.
Các toán tử && và || không giống với các phiên bản chỉ ký tự đơn ở chỗ chúng thực hiện một đánh giá vòng ngắn. Trong biểu thức
a && b
b chỉ được đánh giá nếu a là true. Trong biểu thức
a || b
b chỉ được đánh giá nếu a là false.
Toán tử điều kiện (?:)
Đôi khi cũng được gọi là toán tử ba phần hoặc toán tử câu hỏi, một toán tử điều kiện lựa chọn từ hai biểu thức dựa trên một biểu thức boolean.
int value = (x < 10) ? 15 : 5;
Trong ví dụ này, biểu thức điều kiện (x < 10) được kiểm tra. Nếu đúng, giá trị của toán tử là biểu thức đầu sau dấu chấm hỏi, hay là 15 trong trường hợp này. Nếu là sai, giá trị của toán tử là biểu thức sau dấu hai chấm, hay là 5.
Các toán tử gán
Toán tử gán được dùng để gán một giá trị cho một biến. Có hai dạng thức: phép gán đơn giản và phép gán kết hợp.
Phép gán đơn giản
Phép gán đơn giản được thực hiện trong C# bằng cách sử dụng một dấu bằng đơn lẻ “=”. Để phép gán thành công, phần bên phải của phép gán phải là một kiểu có thể được chuyển đổi mặc nhiên thành kiểu của một biến ở bên trái phép gán.
Phép gán kết hợp
Các toán tử gán kết hợp thực hiện một số thao tác thêm vào trong phép gán đơn giản. Các toán tử kết hợp là:
+= -= *= /= %= &= |= ^= <<= >>=
Một toán tử gán kết hợp
x <op>= y
được tính chính xác như thể được viết là
x = x <op> y
với hai ngoại lệ:
- x chỉ định tính một lần, và việc tính toán đó được sử dụng cho cả phép toán và phép gán.
- Nếu x chứa một lời gọi hàm hay một tham chiếu mảng, nó chỉ được thực hiện một lần.
Dưới các quy tắc chuyển đổi kiểu bình thường, nếu x và y đều là số nguyên ngắn, việc tính x = x + 3;
sẽ phát sinh một lỗi thời gian biên dịch, bởi vì phép cộng được thực hiện trên các giá trị int, và kết quả kiểu int không được chuyển đổi mặc nhiên thành kiểu short. Tuy nhiên, trong trường hợp này, bởi vì short có thể được chuyển đổi mặc nhiên thành int, và ta có thể viết
x = 3;
thao tác được cho phép.
Các toán tử kiểu
Thay vì giao tiếp với các giá trị của một đối tượng, các toán tử kiểu được sử dụng để giao tiếp với kiểu của một đối tượng.
typeof
Toán tử typeof trả ra kiểu của một đối tượng, nó là một thể hiện của lớp System.Type. Typeof là hữu dụng để tránh phải tạo một thể hiện của một đối tượng chỉ để thu được đối tượng type. Nếu một thể hiện hoàn toàn tồn tại, một đối tượng type có thể được thu được bằng cách gọi hàm GetType() trên thể hiện.
Một khi đối tượng type thu được một kiểu, nó có thể được truy vấn bằng cách sử dụng phép phản chiếu (reflection) để thu được thông tin về kiểu. Hãy xem mục “Xâu hơn với phép phản chiếu” trong Chương 31, “Xâu hơn với C#”, để biết thêm thông tin.
is
Toán tử is được sử dụng để xác định có phải một tham chiếu đối tượng có thể được chuyển đổi thành một kiểu xác định hay một giao diện. Cách sử dụng chung nhất của toán tử này là để xác định có phải một đối tượng hỗ trợ một giao diện nhất định:
using System;
interface Iannoy {
void PokeSister(string name);
}
class Brother: IAnnoy {
public void PokeSister(string name) {
Console.WriteLine("Poking {0}", name);
}
}
class BabyBrother {}
class Test {
public static void AnnoyHer(string sister, params object[] annoyers) {
foreach (object o in annoyers) {
if (o is IAnnoy) {
IAnnoy annoyer = (IAnnoy) o;
annoyer.PokeSister(sister);
}
}
}
public static void Main() {
Test.AnnoyHer("Jane", new Brother(), new BabyBrother());
}
}
Đoạn mã này cho ra kết quả sau:
Poking: Jane
Trong ví dụ này, lớp Brother cài đặt giao diện IAnnoy, và lớp BabyBrother thì không. Hàm AnnoyHer() duyệt qua tất cả các đối tượng được chuyển đến cho nó, kiểm tra xem một đối tượng có hỗ trợ IAnnoy, và sau đó gọi hàm PokeSister() nếu đối tượng hỗ trợ giao diện.
as
Toán tử as là tương tự như toán tử is, nhưng thay vì chỉ xác định có phải một đối tượng là một kiểu xác định hay giao diện, nó cũng thực hiện sự chuyển đổi tường minh thành kiểu hay giao diện đó. Nếu đối tượng không thể được chuyển đổi thành kiểu hay giao diện đó, toán tử trả ra giá trị null. Việc sử dụng as là hiệu quả hơn toán tử is, vì toán tử as chỉ cần để kiểm tra kiểu của một đối tượng một lần, trong khi ví dụ sử dụng is kiểm tra kiểu khi toán tử được sử dụng, và một lần nữa khi sự chuyển đổi thực hiện.
Trong ví dụ trước, các dòng sau
if (o is IAnnoy) {
IAnnoy annoyer = (IAnnoy) o;
annoyer.PokeSister(sister);
}
có thể được thay bằng:
IAnnoy annoyer = o as IAnnoy;
if (Annoyer != null)
annoyer.PokeSister(sister);
How useful was this post?
Click on a star to rate it!
Average rating 0 / 5. Vote count: 0
No votes so far! Be the first to rate this post.