Lecture 2
Lecture 2
Tổng quan về C#
1. C# cơ bản
Cú pháp (Syntax)
Ở trong phần thực hành trước, chúng ta đã tạo ra một file Hello.cs cơ bản, biên dich và
chạy chương trình mà chưa phân tích kỹ những nội dung bên trong chương trình. Trong
phần này chúng ta sẽ đi tìm hiểu sâu hơn. Hãy xem lại chương trình sau :
1 using System;
2
3 namespace Hello
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 Console.WriteLine("Hello World using C#!");
10 }
11 }
12 }
(Lưu ý: Đối với kiến trúc mới top-level statement, chúng ta chỉ có một dòng lệnh duy nhất):
Console.WriteLine("Hello World using C#!");
Dòng 1: using System có nghĩa là chúng ta có thể sử dụng các lớp từ namespace System.
Dòng 2: Một dòng trống. C# bỏ qua khoảng trắng. Tuy nhiên, nhiều dòng làm cho mã dễ đọc
hơn.
Dòng 3: namespace được sử dụng để tổ chức mã và nó là một vùng chứa cho các lớp và
các namespace khác.
Dòng 4: Dấu ngoặc nhọn {} đánh dấu sự bắt đầu và kết thúc của một khối mã.
Dòng 5: class là một vùng chứa cho dữ liệu và phương thức, mang lại chức năng cho
chương trình. Mọi dòng mã chạy trong C# phải nằm trong một lớp. Trong ví dụ của chúng ta,
chúng ta đặt tên lớp là Program.
Dòng 7: Một điều khác luôn xuất hiện trong chương trình C# là phương thức Main. Bất kỳ mã
nào trong dấu ngoặc nhọn {} của nó sẽ được thực thi.
Dòng 9: Console là một lớp của namespace System, có phương thức WriteLine() được sử
dụng để xuất/in văn bản. Trong ví dụ của chúng ta, nó sẽ xuất "Hello World using C#!".
Nếu bỏ qua dòng using System, ta sẽ phải viết System.Console.WriteLine() để in/xuất văn
bản.
Lưu ý: Mỗi câu lệnh trong C# kết thúc bằng dấu chấm phẩy ;.
Lưu ý: C# phân biệt chữ hoa chữ thường; "MyClass" và "myclass" có ý nghĩa khác nhau.
Lưu ý: Không giống như Java, tên của tệp C# không bắt buộc phải khớp với tên lớp, nhưng
thường thì nó khớp (để tổ chức tốt hơn). Khi lưu tệp, lưu nó bằng một tên phù hợp và thêm
".cs" vào cuối tên tệp.
C# Xuất/In (Output)
Để xuất giá trị hoặc in văn bản trong C#, ta có thể sử dụng phương thức WriteLine(). Ví dụ
Console.WriteLine("Hello World using C#!");
Ta có thể thêm bao nhiêu phương thức WriteLine() tùy thích. Lưu ý rằng nó sẽ thêm một dòng
mới cho mỗi phương thức. Ví dụ
Console.WriteLine("Hello World!");
Console.WriteLine("I am Learning C#");
Console.WriteLine("It is awesome!");
Ta cũng có thể xuất số và thực hiện các phép tính toán học:
Console.WriteLine(3 + 3);
Ngoài ra còn có phương thức Write() tương tự như WriteLine(). Điểm khác biệt duy nhất là nó
không chèn dòng mới vào cuối đầu ra:
Sự khác biệt giữa phương thức WriteLine() và Write():
Sự khác biệt chính giữa WriteLine() và Write() là phương thức Write() chỉ in chuỗi được cung
cấp cho nó, trong khi phương thức WriteLine() in chuỗi và di chuyển đến đầu dòng tiếp theo.
Chúng ta hãy xem ví dụ dưới đây để hiểu sự khác biệt giữa các phương pháp này.
Console.WriteLine("Prints on ");
Console.WriteLine("New line");
Console.Write("Prints on ");
Console.Write("Same line");
Kết quả:
Prints on
New line
Prints on Same line
C# Nhập (Input)
Chúng ta sẽ sử dụng Console.ReadLine() để lấy đầu vào từ người dùng. Trong ví dụ sau, người
dùng có thể nhập tên đăng nhập của họ, tên này sẽ được lưu trữ trong biến userName. Sau
đó, chúng ta in giá trị của userName:
// Tạo một biến chuỗi và lấy đầu vào từ bàn phím, sau đó lưu trữ trong biến
string userName = Console.ReadLine();
// In giá trị của biến (userName), sẽ hiển thị giá trị đầu vào
Console.WriteLine("Username is: " + userName);
Lưu ý : Phương thức Console.ReadLine() trả về một chuỗi. Do đó, ta không thể lấy thông tin
từ một kiểu dữ liệu khác, chẳng hạn như int. Chương trình sau sẽ gây ra lỗi:
Biến (Variable)
Biến là các vùng chứa để lưu trữ các giá trị dữ liệu. Trong C#, có các loại biến khác nhau
(được định nghĩa với các từ khóa khác nhau), ví dụ:
- int - lưu trữ các số nguyên, không có phần thập phân, chẳng hạn như 123 hoặc -123
- double - lưu trữ các số thực có dấu phẩy động, có phần thập phân, chẳng hạn như
19.99 hoặc -19.99
- char - lưu trữ các ký tự đơn, chẳng hạn như 'a' hoặc 'B'. Các giá trị kiểu char được
bao quanh bởi dấu nháy đơn
- string - lưu trữ văn bản, chẳng hạn như "Hello World". Các giá trị kiểu string được
bao quanh bởi dấu nháy kép
- bool - lưu trữ các giá trị với hai trạng thái: true hoặc false
Để tạo một biến, ta phải chỉ định loại và gán cho nó một giá trị: Cú pháp như sau:
type variableName = value;
Trong đó, type là một kiểu dữ liệu trong C# (chẳng hạn như int hoặc string), và variableName
là tên của biến (chẳng hạn như x hoặc name). Dấu bằng (=) được sử dụng để gán giá trị cho
biến.
Ví dụ:
Tạo một biến có tên name kiểu string và gán cho nó giá trị "John":
Tạo một biến có tên myNum kiểu int và gán cho nó giá trị 15:
Ta cũng có thể khai báo một biến mà không gán giá trị, và gán giá trị sau:
int myNum;
myNum = 15;
Console.WriteLine(myNum);
Lưu ý rằng nếu ta gán một giá trị mới cho một biến đã tồn tại, nó sẽ ghi đè giá trị trước đó:
Cách khai báo các biến của các kiểu dữ liệu khác:
int myNum = 5;
double myDoubleNum = 5.99D;
char myLetter = 'D';
bool myBool = true;
string myText = "Hello";
Hằng số
Nếu ta không muốn người khác ghi đè lên các giá trị đã tồn tại, ta có thể thêm từ khóa const
trước kiểu dữ liệu của biến. Điều này sẽ khai báo biến như là "hằng số", nghĩa là không thể
thay đổi và chỉ có thể đọc: Ví dụ
Từ khóa const hữu ích khi ta muốn một biến luôn lưu trữ cùng một giá trị, để người khác (hoặc
chính ta) không làm hỏng mã nguồn. Ví dụ: hằng số là PI (3.14159...).
Lưu ý: Ta không thể khai báo một biến hằng mà không gán giá trị. Lỗi gặp phải: A const field
requires a value to be provided
Các quy tắc chung để đặt tên biến
- Tên có thể chứa chữ cái, chữ số và ký tự gạch dưới (_)
- Tên phải bắt đầu bằng một chữ cái hoặc dấu gạch dưới
- Tên nên bắt đầu bằng chữ thường và không được chứa khoảng trắng
- Tên phân biệt chữ hoa chữ thường ("myVar" và "myvar" là các biến khác nhau)
- Các từ dành riêng (như các từ khóa trong C#, chẳng hạn như int hoặc double) không
thể được sử dụng làm tên.
class A
{
public static int x;
int y;
void F(int[] v, int a, ref int b, out int c, in int d)
{
int i = 1;
c = a + b++ + d;
}
}
x là một biến tĩnh (static variable), y là một biến thể hiện (instance variable), v[0] là một phần
tử mảng (array element), a là một tham số giá trị (value parameter), b là một tham số tham
chiếu (reference parameter), c là một tham số đầu ra (output parameter), d là một tham số
đầu vào (input parameter), và i là một biến cục bộ (local variable).
Toán tử
Toán tử số học: được sử dụng để thực hiện các phép toán toán học thông thường:
Ts có thể sử dụng các điều kiện này để thực hiện các hành động khác nhau cho các quyết
định khác nhau. C# có các câu lệnh điều kiện sau:
- Sử dụng if để chỉ định một khối mã sẽ được thực thi nếu một điều kiện xác định là
đúng. Ví dụ
int x = 20;
int y = 18;
if (x > y) // Điều kiện
{
// Khối mã sẽ được thực thi nếu điều kiện là đúng
Console.WriteLine("x is greater than y");
}
- Sử dụng else để chỉ định một khối mã sẽ được thực thi nếu cùng điều kiện đó là sai.
Cũng có một cách viết tắt cho if else, được gọi là toán tử ba ngôi vì nó bao gồm ba toán hạng.
Nó có thể được sử dụng để thay thế nhiều dòng mã bằng một dòng duy nhất. Thường được
sử dụng để thay thế các câu lệnh if else đơn giản:
- Sử dụng else if để chỉ định một điều kiện mới để kiểm tra, nếu điều kiện đầu tiên là
sai.
- Sử dụng switch để chỉ định nhiều khối mã thay thế sẽ được thực thi.
int day = 4;
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
case 4:
Console.WriteLine("Thursday");
break;
case 5:
Console.WriteLine("Friday");
break;
case 6:
Console.WriteLine("Saturday");
break;
case 7:
Console.WriteLine("Sunday");
break;
}
// Outputs "Thursday" (day 4)
Toán tử logic
Toán tử logic được sử dụng để xác định logic giữa các biến hoặc giá trị:
int i = 0;
while (i < 5) // condition
{
// khối mã sẽ được thực thi nếu điều kiện là đúng
Console.WriteLine(i);
i++;
}
Vòng lặp do/while: Vòng lặp do/while là một biến thể của vòng lặp while. Vòng lặp này sẽ thực
thi khối mã một lần trước khi kiểm tra điều kiện, sau đó sẽ lặp lại miễn là điều kiện là đúng.
int i = 0;
do
{
// khối mã sẽ được thực thi
Console.WriteLine(i);
i++;
}
while (i < 5);
Vòng lặp sẽ luôn được thực thi ít nhất một lần, ngay cả khi điều kiện là sai, vì khối mã được
thực thi trước khi điều kiện được kiểm tra:
Vòng lặp for: Khi ta biết chính xác số lần muốn lặp qua một khối mã, hãy sử dụng vòng lặp
for thay vì vòng lặp while:
Cũng có thể đặt một vòng lặp bên trong một vòng lặp khác. Điều này được gọi là vòng lặp
lồng nhau. Vòng lặp "bên trong" sẽ được thực thi một lần cho mỗi lần lặp của vòng lặp "bên
ngoài":
Vòng lặp foreach: Cũng có một vòng lặp foreach, được sử dụng riêng để lặp qua các phần
tử trong một mảng:
Câu lệnh continue phá vỡ một lần lặp (trong vòng lặp), nếu một điều kiện xác định xảy ra, và
tiếp tục với lần lặp tiếp theo trong vòng lặp. Ví dụ này bỏ qua giá trị 4:
Ta cũng có thể sử dụng break và continue trong các vòng lặp while:
int i = 0;
while (i < 10)
{
Console.WriteLine(i);
i++;
if (i == 4)
{
break;
}
}
try
{
// Khối mã để thử
}
catch (Exception e)
{
// Khối mã để xử lý lỗi
}
Xem xét ví dụ sau, tạo một mảng gồm ba số nguyên: Điều này sẽ tạo ra lỗi, vì
myNumbers[10] không tồn tại.
try
{
int[] myNumbers = { 1, 2, 3 };
Console.WriteLine(myNumbers[10]);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Kết quả sẽ là :
Index was outside the bounds of the array.
Ta cũng có thể xuất ra thông báo lỗi thủ công:
try
{
int[] myNumbers = { 1, 2, 3 };
Console.WriteLine(myNumbers[10]);
}
catch (Exception e)
{
Console.WriteLine("Something went wrong.");
}
Finally
Câu lệnh finally cho phép ta thực thi mã, sau try...catch, bất kể kết quả thế nào:
Ví dụ:
try
{
int[] myNumbers = { 1, 2, 3 };
Console.WriteLine(myNumbers[10]);
}
catch (Exception e)
{
Console.WriteLine("Something went wrong.");
}
finally
{
Console.WriteLine("The 'try catch' is finished.");
}
2. Kiểu dữ liệu
Một biến trong C# phải có một kiểu dữ liệu được xác định. Ví dụ
Kiểu dữ liệu xác định kích thước và loại giá trị của biến. Việc sử dụng đúng kiểu dữ liệu cho
biến tương ứng là quan trọng để tránh lỗi, tiết kiệm thời gian và bộ nhớ, và cũng làm cho mã
dễ duy trì và đọc hơn. Các kiểu dữ liệu phổ biến nhất là:
Kiểu dữ liệu Kích thức Mô tả
int 4 bytes Lưu trữ các số nguyên từ -2,147,483,648 đến 2,147,483,647
long 8 bytes Lưu trữ các số nguyên từ -9,223,372,036,854,775,808 đến
9,223,372,036,854,775,807
float 4 bytes Lưu trữ các số thực. Đủ để lưu trữ 6 đến 7 chữ số thập phân
double 8 bytes Lưu trữ các số thực. Đủ để lưu trữ 15 chữ số thập phân
bool 1 bit Lưu trữ giá trị true hoặc false
char 2 bytes Lưu trữ một ký tự duy nhất, bao quanh bởi dấu nháy đơn
string 2 bytes per Lưu trữ một chuỗi ký tự, bao quanh bởi dấu nháy kép
character
Kiểu Số
Các kiểu số được chia thành hai nhóm:
- Kiểu số nguyên lưu trữ các số nguyên, dương hoặc âm (chẳng hạn như 123 hoặc -
456), không có phần thập phân. Các kiểu hợp lệ là int và long. Việc sử dụng kiểu nào
phụ thuộc vào giá trị số. Kiểu long kết thúc giá trị bằng chữ L
- Kiểu số thực đại diện cho các số có phần thập phân, chứa một hoặc nhiều chữ số
thập phân. Các kiểu hợp lệ là float (kết thúc giá trị bằng chữ F) và double (kết thúc
giá trị bằng chữ D). Một số thực cũng có thể là một số khoa học với một "e" để chỉ ra
lũy thừa của 10:
float f1 = 35e3F;
double d1 = 12E4D;
Console.WriteLine(f1);
Console.WriteLine(d1);
Mặc dù có nhiều kiểu số trong C#, nhưng int (cho số nguyên) và double (cho số thực) là hai
kiểu được sử dụng phổ biến nhất.
Kiểu ký tự (char)
Kiểu dữ liệu char được sử dụng để lưu trữ một ký tự duy nhất. Ký tự phải được bao quanh
bởi dấu nháy đơn, như 'A' hoặc 'c':
char myGrade = 'B';
Console.WriteLine(myGrade);
String interpolation: Nội suy chuỗi thay thế giá trị của các biến vào các chỗ trống trong một
chuỗi
Cũng lưu ý rằng ta phải sử dụng ký hiệu đô la ($) khi sử dụng phương pháp nội suy chuỗi.
int myInt = 9;
double myDouble = myInt; // Ép kiểu tự động: int sang double
Console.WriteLine(myInt); // Xuất ra 9
Console.WriteLine(myDouble); // Xuất ra 9
Ép kiểu tường minh : Ép kiểu tường minh phải được thực hiện thủ công bằng cách đặt kiểu
dữ liệu trong dấu ngoặc đơn trước giá trị:
Ví dụ
Phương pháp chuyển đổi kiểu dữ liệu : Cũng có thể chuyển đổi kiểu dữ liệu tường minh
bằng cách sử dụng các phương thức tích hợp sẵn, như Convert.ToBoolean,
Convert.ToDouble, Convert.ToString, Convert.ToInt32 (int) và Convert.ToInt64 (long):
Ví dụ
Mảng (Array)
Mảng được sử dụng để lưu trữ nhiều giá trị trong một biến duy nhất, thay vì khai báo các
biến riêng lẻ cho mỗi giá trị.
Để khai báo một mảng, xác định kiểu biến với dấu ngoặc vuông:
string[] cars;
Để chèn giá trị vào nó, chúng ta có thể sử dụng một mảng trực tiếp - đặt các giá trị trong
một danh sách phân cách bằng dấu phẩy, bên trong dấu ngoặc nhọn:
Ta truy cập một phần tử mảng bằng cách tham chiếu đến số chỉ mục. Dòng lệnh sau truy
cập giá trị của phần tử đầu tiên trong mảng cars:
Lưu ý: Các chỉ mục của mảng bắt đầu từ 0: [0] là phần tử đầu tiên. [1] là phần tử thứ hai, v.v.
Để thay đổi giá trị của một phần tử cụ thể, tham chiếu đến số chỉ mục:
Để tìm ra số lượng phần tử của một mảng, sử dụng thuộc tính Length:
// Tạo một mảng gồm bốn phần tử và thêm giá trị ngay lập tức
string[] cars = new string[4] {"Volvo", "BMW", "Ford", "Mazda"};
// Tạo một mảng gồm bốn phần tử mà không chỉ định kích thước
string[] cars = new string[] {"Volvo", "BMW", "Ford", "Mazda"};
// Tạo một mảng gồm bốn phần tử, bỏ qua từ khóa new và không chỉ định kích thước
string[] cars = {"Volvo", "BMW", "Ford", "Mazda"};
[Thực hành 1] Nhập số với điều kiện
TH1: Viết chương trình nhập vào số nguyên dương
TH5: Viết chương trình nhập vào số nguyên dương với điều kiện (2 < N < 100).
int N = int.Parse(Console.ReadLine());
while (N < =2 || N >= 100){
N = int.Parse(Console.ReadLine());
};
class Program
{
static void MyMethod()
{
// code to be executed
}
}
Giải thích:
- MyMethod() là tên của phương thức
- static có nghĩa là phương thức thuộc về lớp Program chứ không phải một đối tượng
của lớp Program.
- void có nghĩa là phương thức này không có giá trị trả về.
Một phương thức có thể được gọi nhiều lần → đó chính là ý nghĩa của việc tái sử dụng:
int N = int.Parse(Console.ReadLine());
int[] arr = new int[N];
int i = 0;
while (i < N)
{
arr[i] = int.Parse(Console.ReadLine());
i++;
}
VD2: Viết chương trình yêu cầu nhập một mảng các số nguyên có N phần tử với điều kiện 0
< N < 100
int N = int.Parse(Console.ReadLine());
while (N <= 0 || N >= 100)
{
N = int.Parse(Console.ReadLine());
}
int[] arr = new int[N];
int i = 0;
while (i < N)
{
arr[i] = int.Parse(Console.ReadLine());
i++;
}
VD3: Viết chương trình yêu cầu nhập một ma trận (matrix) có n hàng (rows) và m cột (cols)
VD4: Viết chương trình yêu cầu nhập một ma trận (matrix) vuông (số hàng = số cột)
VD6: Viết chương trình yêu cầu nhập hai số nguyên và tính tổng của chúng
int a = int.Parse(Console.ReadLine());
int b = int.Parse(Console.ReadLine());
int sum = a + b;
Console.WriteLine("Tổng hai số là: " + sum);
VD7: Viết chương trình nhập số nguyên N, kiểm tra và xuất kết quả N là số chẵn/lẻ
int N = int.Parse(Console.ReadLine());
if (N % 2 == 0)
{
Console.WriteLine("N là số chẵn");
}
else
{
Console.WriteLine("N là số lẻ");
}
VD8: Viết chương trình nhập vào ba cạnh của một tam giác và kiểm tra xem tam giác đó là
tam giác đều, tam giác cân hay tam giác thường
if (a == b && b == c)
{
Console.WriteLine("Tam giác là tam giác đều");
}
else if (a == b || b == c || a == c)
{
Console.WriteLine("Tam giác là tam giác cân");
}
else
{
Console.WriteLine("Tam giác là tam giác thường");
}
VD9 : Viết chương trình nhập vào 2 số a và b thực hiện tìm ước số chung lớn nhất của 2 số
đó
VD10 : Viết chương trình giải phương trình bậc nhất có dạng ax + b = 0, với các hệ số a, b
được nhập từ bàn phím