Bài 3: Lệnh và toán tử trong Lập trình C# 7.0 - Visual Studio 2017

Bài 3: Lệnh và toán tử trong Lập trình C# 7.0 - Visual Studio 2017
access_time 11/20/2017 12:00:00 AM
person Nguyễn Mạnh Hùng
Một chương trình bao gồm một chuỗi các câu lệnh. Khi chạy chương trình, các câu lệnh sẽ được thực thi, trình tự thực thi của các câu lệnh thông thường từ trái sang phải và từ trên xuống dưới.
1. Định nghĩa Lệnh và Khối lệnh
Thông thường, lệnh là một tập hợp các biến, các hằng số, các toán tử kết hợp với các từ khóa (những cụm từ mà ngôn ngữ lập trình quy ước) để mô tả sự logic trong điều khiển và qua đó, máy tính sẽ thực thi “những logic” này đạt đến kết quả cuối cùng xem như mục tiêu điều khiển của chương trình.
Trong C#, ký hiệu dùng để mô tả cho sự kết thúc một lệnh chính là dấu “;.
Ví dụ :
int x; // một câu lệnh
x = 10; // 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.
Trong C# một tập hợp các câu lệnh (Nhiều hơn một lệnh) người ta gọi là một khối lệnh, khối lệnh thực thi một mục đích logic nào đó. Một khối lệnh bắt đầu bằng dấu “{” và kết thúc bằng dấu “}”;
Ví dụ : Khối lệnh tính diện tích của đường tròn.
Hình số 1 : Khối lệnh
Với 1 lệnh sử dụng trong 1 chương trình viết bởi C#, thông thường dùng để mô tả cho các hành động nhập dữ liệu, xử lý và xuất dữ liệu để đáp ứng cho 1 mục đích cụ thể nào đó của chương trình, các lệnh này có thể bao gồm: Đặc trưng của khối lệnh đó là phạm vi thực hiện trong khối lệnh, một biến được khai báo trong khối lệnh được gọi là biến cục bộ, có nghĩa là biến này chỉ có tác dụng trong khối lệnh đó.
Chú ý : Trong C# lập trình viên không thể khai báo tên biến trong khối lệnh trùng với tên biến nằm ngoài khối lệnh chứa khối lệnh đó.
Ví dụ :
Hình số 2 : Khai báo biến cục bộ trong khối lệnh
Tuy nhiên với các khối lệnh cùng cấp (Các khối lệnh riêng biệt cùng nằm trong khối lệnh cha) thì hoàn toàn có thể khai báo các biến trùng tên một cách thỏa mái.
2. Các dạng lệnh trong C#
Các lệnh trong ngôn ngữ C# cũng tương tự như các lệnh trong ngôn ngữ lập trình C/C++, C# phân chia lệnh thành 7 loại khác nhau :
2.1 Cấu trúc lựa chọn (Selection Statements) :
Là các cấu trúc lệnh rẽ nhánh như if, if …else …, switch … Các cấu trúc này thường được sử dụng trong những tình huống giả định để thể hiện tính linh hoạt, thông minh trong logic xử lý của chương trình.

Ví dụ mô tả cấu trúc if-else thông qua hình vẽ sau :


Hình số 3 : Mô tả cấu trúc lệnh if-else

Khi lập trình với cấu trúc if-else đối với C# được thực hiện hóa như sau :


Hình số 4 : Mã lệnh thực hiện cấu trúc lệnh if-else
2.2 Cấu trúc lặp (Interaction Statements):
Là các cấu trúc for, foreach, do.. while, while … Cấu trúc này nhằm tổ chức thực thi các câu lệnh có mục đích lặp đi lặp lại nhiều lần. sử dụng cấu trúc lặp trong chương trình làm cho việc mô tả mã lệnh (code) trở nên ngắn gọn hơn rất nhiều, cách biểu diễn của chương trình cũng nhờ đó mà trong sáng, dễ hiểu hơn.
Ví dụ mô tả cấu trúc for thông qua hình vẽ  sau:

Hình số 5 : Mô tả cấu trúc lệnh lặp
Khi lập trình với cấu trúc for đối với C# được thực hiện hóa như sau :


Hình số 6: Mã lệnh thực hiện cấu trúc for
 
2.3 Lệnh “nhảy” (Jump Statements) :
Các lệnh điều khiển với mục đích phá vỡ quy luật xử lý tuần tự của chương thường thấy là : break, continue, default, goto, return yield. Một số lệnh dạng này không được khuyến khích sử dụng trong các cấu trúc chương trình viết bằng ngôn ngữ lập trình hiện đại (ví dụ như lệnh goto) do nó có khả năng làm phá vỡ tính logic của thuật toán, có thể dẫn đến khó kiểm soát, tuy nhiên 1 số lệnh vẫn cần phải sử dụng như lệnh return để trả về từ trong 1 hàm, 1 phương thức. Lệnh break bỏ qua thực hiện các tình huống phía sau của tình huống đã chọn đối với cấu trúc switch.
Ví dụ :
Hình số 7 : Mô tả lệnh nhảy
2.4 Lệnh giám sát lỗi (Exception handling Statement):  
Trong quá trình xây dựng chương trình, đôi khi có những tình huống mà lập trình viên không thể lường trước, những tình huống bất khả kháng xảy ra tại thời điểm chương trình thực thi. Để giải quyết vấn đề này, C# cung cấu trúc kiểm soát thực thi chương trình dưới dạng try … catch, try … catch … finally.
Ví dụ :
Hình số 8 : Mô tả lệnh giám sát lỗi
2.5 Lệnh kiểm tra tính an toàn cho các phép toán (Checked and Unchecked Statements):
Trong quá trình thực thi, thông thường ứng với các biến thuộc các kiểu dữ liệu được định nghĩa trong chương trình chỉ có thể chứa được giá trị trong khoảng cho phép tương ứng với kiểu dữ liệu mà nó thuộc về. Ví dụ 1 biến kiểu byte chỉ có thể chứa được giá trị số nguyên dương lớn nhất là 255, tuy nhiên trong những hoàn cảnh đặc biệt nào đó, giá trị gán cho biến của kiểu này có nguy cơ lớn hơn 255 dẫn đến hiện tượng tràn số trong bộ nhớ (overflow). Để giải quyết tình trạng này, C# cung cấp cấu trúc checked … unchecked
Ví dụ :
 Hình số 9 : Mô tả lệnh check và uncheck
2.6 Lệnh Fixed và cơ chế mã không an toàn (Fixed and Unsafe statement):
Để giúp cho việc quản lý bộ nhớ một cách hiệu quả và “an toàn” trong quá trình mà chương trình thi hành, .NET Framework cung cấp “bộ dọn rác” (Garbage collector) để thu gom và giải phóng tất cả các vùng nhớ dư thừa do chương trình sinh ra trong quá trình thực thi. Trong những tình huống cụ thể nào đó, bạn chắc chắn rằng việc phối hợp quản lý các vùng nhớ trong chương trình của mình một cách chuẩn xác thì có thể dùng từ khóa Fixed hoặcUnsafe để có thể chỉ ra những vùng nhớ mà mình quy ước để Garbage collector bỏ qua trong quá trình thu gom của nó hay như việc sử dụng các truy vấn trực tiếp đến các vùng nhớ thông qua toán tử địa chỉ, con trỏ (&, *)
2.7 Lệnh Lock (Lock statement):
Trong quá trình chương trình hoạt động, đôi khi việc gọi thi hành 1 tập lệnh có khả năng xảy ra đồng thời tại 1 thời điểm do nhiều tiểu trình đang thực thi song hành tại thời điểm đó. Đôi khi điều này có thể dẫn đến bế tắc cho quá trình xử lý (deadlock for processing). Lệnh lock được cung cấp trong C# với mục đích giới hạn truy cập đối với 1 tập lệnh nào đó của chương trình, tức là tại 1 thời điểm chỉ có 1 đối tượng (Tiểu trình) được phép sử dụng tập lệnh đó mà thôi.
3. 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:
Ví dụ :
x = 30;
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à 30 cho biến x . 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. Do x = 30 là một biểu thức được định giá trị là 30 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:
x = y = 30;
Lệnh này sẽ được thực hiện từ bên phải sang khi đó biến x sẽ nhận được giá trị là 30 và tiếp sau đó thì y cũng được nhận giá trị là 30. Do vậy cả hai biến đều cùng nhận một giá trị là 30. Có thể dùng lệnh trên để khởi tạo nhiều biến có cùng một giá trị như:
y = y = z = a = 30;
4. Toán tử trong C#
C# cung cấp khá đa dạng các phép toán với mục đích giúp cho việc đơn giản hóa biểu thức sử dụng trong chương trình đồng thời tăng cường tính hiệu quả cho việc thực thi các lệnh. Dưới đây là danh sách các dạng phép toán có thể sử dụng trong 1 chương trình viết bằng C# 4.1 Toán tử số học :
Ngôn ngữ C# cung cấp năm toán tử toán học, bao gồm bốn toán tử đầu các phép toán cơ bản. Toán tử cuối cùng là toán tử chia nguyên lấy phần dư. Chúng ta sẽ tìm hiểu chi tiết các phép toán này trong phần tiếp sau. 4.2 Toán tử tăng giảm
Khi sử dụng các biến số ta thường có thao tác là cộng một giá trị vào biến, trừ đi một giá trị từ biến đó, hay thực hiện các tính toán thay đổi giá trị của biến sau đó gán giá trị mới vừa tính toán cho chính biến đó (Tính toán và gán trả lại).
Ví dụ :
x = x + 1;
Do việc tăng hay giảm giá trị của một biến rất thường xảy ra trong khi tính toán nên C# cung cấp các phép toán tự gán. Bảng sau liệt kê phép toán tự gán :
Ví dụ :
x += 100; // x sẽ thực hiện phép toán x = x + 100
y -= 100; // y sẽ thực hiện phép toán y = y – 100;
Do việc tăng hay giảm 1 rất phổ biến trong lập trình nên C# cung cấp hai toán tử đặc biệt là tăng một (++) hay giảm một (--).
Toán tử tăng giảm tiền tố và tăng giảm hậu tố
Giả sử muốn kết hợp các phép toán như gia tăng giá trị của một biến và gán giá trị của biến cho biến thứ hai, ta viết như sau:
x= y++;
 
Câu hỏi được đặt ra là gán giá trị trước khi cộng hay gán giá trị sau khi đã cộng. Hay nói cách khác giá trị ban đầu của biến y là 10, sau khi thực hiện ta muốn giá trị của x là 10, y là 11, hay x là 11, y cũng 11?
Để giải quyết yêu cầu trên C# cung cấp thứ tự thực hiện phép toán tăng/giảm với phép toán gán, thứ tự này được gọi là tiền tố (prefix) hay hậu tố (postfix). Do đó ta có thể viết:
x = y++; // Hậu tố
Khi lệnh này được thực hiện thì phép gán sẽ được thực hiện trước tiên, sau đó mới đến phép toán tăng.
Kết quả là x = 10 và y = 11.
Còn đối với trường hợp tiền tố:
x = ++y; Khi đó phép tăng sẽ được thực hiện trước tức là giá trị của biến y sẽ là 11 và cuối cùng phép gán được thực hiện. Kết quả cả hai biến x và y điều có giá trị là 11.
Ví dụ :
Hình số 10 : Toán tử tăng giảm
4.2 Toán tử quan hệ.
Thường dùng cho phép so sánh, thể hiện sự tương quan giữa 2 giá trị. Kết quả của phép toán quan hệ thường trả về giá trị thuộc kiểu bool tương ứng với 2 trạng thái true (Đúng), false (sai).
Ví dụ : Ví dụ toán tử so sánh lớn hơn (>) trả về giá trị là true nếu giá trị bên trái của toán tử lớn hơn giá trị bên phải của toán tử. Do vậy 5 > 2 trả về một giá trị là true, trong khi
3 > 5 trả về giá trị false.
Bảng mô tả toán tử quan hệ
4.3 Toán tử logic
Thường dùng cho mục đích phối hợp nhiều biểu thức logic với nhau để cho ra 1 giá trị logic duy nhất. Toán tử logic bao gồm && : Khi các biểu thức logic đồng thời cùng mang giá trị true thì kết quả phối hợp của chúng sẽ trả về là true. Trong trường hợp có 1 biểu thức false hoặc tất cả cùng false thì kết quả phối hợp sẽ là false.
Áp dụng toán tử logic trong các câu lệnh if, while …,do…while …
Bàng dưới đâu mô tả toán tử logic của 2 biến x,y làm minh họa.
5. Độ ưu tiên của toán tử
Trình biên dịch phải xác định thứ tự thực hiện các toán tử trong trường hợp một biểuthức có nhiều phép toán, giả sử, có biểu thức sau:
x = 10+8*3;
Biểu thức trên có ba phép toán để thực hiện bao gồm (=, +,*). Ta thử xét các phép toán theo thứ tự từ trái sang phải, đầu tiên là gán giá trị 10 cho biến x, sau đó cộng 8 vào 10 là 18 cuối cùng là nhân với 3, kết quả trả về là 54, điều này thật sự có vấn đề, không đúng với mục đích yêu cầu của chúng ta.
Do vậy việc xây dựng một trình tự xử lý các toán tử là hết sức cần thiết. Các luật về độ ưu tiên xử lý sẽ bảo trình biên dịch biết được toán tử nào được thực hiện trước trong biểu thức.Tương tự như trong phép toán đại số thì phép nhân có độ ưu tiên thực hiện trước phép toán cộng, do vậy 10+8*3 cho kết quả là 34 đúng hơn kết quả 54. Và cả hai phép
toán cộng và phép toán nhân điều có độ ưu tiên cao hơn phép gán. Như vậy trình biên dịch sẽ thực hiện các phép toán rồi sau đó thực hiện phép gán ở bước cuối cùng. Kết quả đúng của câu lệnh trên là biến x sẽ nhận giá trị là 34.
Trong ngôn ngữ C#, dấu ngoặc được sử dụng để thay đổi thứ tự xử lý, điều này cũng giống trong tính toán đại số.
Khi đó muốn kết quả 54 cho biến x có thể viết:
x = (10+8) * 3;
Biểu thức trong ngoặc sẽ được xử lý trước và sau khi có kết quả là 18 thì phép nhân được
thực hiện.
Bảng dưới dây liệt kê thứ tự ưu tiên trong lập trình C#
6. Vấn đề Boxing và Unboxing
Khi cần phải chuyển dữ liệu từ dạng cơ bản (Primitive datatype) thành object, c# sẽ tạo ra một “box object” để chứa giá trị. Quá trình này gọi là Boxing.
Ví dụ :
int num = 100;
Object boxed = num;
Hình sau đây mô tả quá trình thực hiện Boxing.
Hình số 11 : Mô tả Boxing
Trường hợp ngược lại, khi giá trị của 1 “box object” được chuyển ngược về dữ liệu cơ bản thì gọi là unboxing
Ví dụ :
int num = 100;
Object boxed = num;
int unbox = (int)boxed;
HÌnh sau đây mô tả quá trình thực hiện Unboxing.
Hình số 12 : Mô tả UnBoxing

vertical_align_top
share
Chat...