CHƢƠNG 1– TỔNG QUAN VỀ FRAMEWORK .NET
2.4 Các kiểu dữ liệu có cấu trúc
2.4.3 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ê).
Chƣơng 2 – Ngơn ngữ lập trình c#
51 [thuộc tính] [bổ sung] enum <tên liệt kê> [:kiểu cơ sở]
{danh sách các thành phần liệt kê};
Chú ý:
- 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ự
- 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ự
- 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.
Ví dụ: enum Thu:int { ChuNhat=0, ThuHai=1, ThuBa=2, ThuTu=3, ThuNam=4,
Chƣơng 2 – Ngơn ngữ lập trình c# 52 ThuSau=5, ThuBay=6, } Ví dụ: enum Thu { ChuNhat, ThuHai, ThuBa, ThuTu, ThuNam, ThuSau, ThuBay, }
ChuNhat=0, ThuHai=1, ThuBa=2, ThuTu=3, ThuNam=4, ThuSau=5, ThuBay=6
Chƣơng 2 – Ngơn ngữ lập trình c# 53 enum Thu { ChuNhat=0, ThuHai=10, ThuBa, ThuTu, ThuNam=20, ThuSau, ThuBay, }
ChuNhat=0, ThuHai=10, ThuBa=11, ThuTu=12, ThuNam=20,
ThuSau=21, ThuBay=22
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:
Chƣơng 2 – Ngơn ngữ lập trình c#
54 2.4.4 Khơng gian tên
Nhƣ chúng ta đã biết .NET cung cấp một thƣ viện các lớp đồ sộ và thƣ viện này có tên là FCL (Framework Class Library). Trong đó Console chỉ là một lớp nhỏ trong hàng ngàn lớp trong thƣ viện. Mỗi lớp có một tên riêng, vì vậy FCL có hàng ngàn tên nhƣ ArrayList, Dictionary, FileSelector,… Điều này làm nảy sinh vấn đề, ngƣời lập trình khơng thể nào nhớ hết đƣợc tên của các lớp trong .NET Framework. Tệ hơn nữa là sau này có thể ta tạo lại một lớp trùng với lớp đã có chẳng hạn. Ví dụ trong q trình phát triển một ứng dụng ta cần xây dựng một lớp từ điển và lấy tên là Dictionary, và điều này dẫn đến sự tranh chấp khi biên dịch vì C# chỉ cho phép một tên duy nhất.
Chắc chắn rằng khi đó chúng ta phải đổi tên của lớp từ điển mà ta vừa tạo thành một cái tên khác chẳng hạn nhƣ myDictionary. Khi đó sẽ làm cho việc phát triển các ứng dụng trở nên phức tạp, cồng kềnh. Đến một sự phát triển nhất định nào đó thì chính là cơn ác mộng cho nhà phát triển.
Giải pháp để giải quyết vấn đề này là việc tạo ra một namespace, namsespace sẽ hạn chế phạm vi của một tên, làm cho tên này chỉ có ý nghĩa trong vùng đã định nghĩa.
Tƣơng tự nhƣ vậy ta cứ tạo các namespace để phân thành các vùng cho các lớp trùng tên không tranh chấp với nhau.
Tƣơng tự nhƣ vậy, .NET Framework có xây dựng một lớp Dictionary bên trong namespace System.Collections, và tƣơng ứng ta có thể tạo một lớp Dictionary khác nằm trong namespace ProgramCSharp.DataStructures, điều này hoàn tồn khơng dẫn đến sự tranh chấp với nhau.
Chƣơng 2 – Ngơn ngữ lập trình c#
55 Để làm cho chƣơng trình gọn hơn, và khơng cần phải viết từng namespace cho từng đối tƣợng, C# cung cấp từ khóa là using, sau từ khóa này là một namespace hay
subnamespace với mô tả đầy đủ trong cấu trúc phân cấp của nó.
Ta có thể dùng dịng lệnh: using System; ở đầu chƣơng trình và khi đó trong
chƣơng trình nếu chúng ta có dùng đối tƣợng Console thì khơng cần phải viết đầy đủ: System.Console. mà chỉ cần viết Console. thôi.
using < Tên namespace >
Để tạo một namespace dùng cú pháp sau:
namespace <Tên namespace>
{ < Định nghĩa lớp A> < Định nghĩa lớp B > ..... } 2.5 Hàm
Khi thực thi một đoạn code nào nó nhiều lần, thay vì phải copy đi copy lại đoạn code đó nhiều lần, dẫn đến chƣơng trình chúng ta bị trùng lặp code rất nhiều, trong
Chƣơng 2 – Ngơn ngữ lập trình c#
56 c# có function cho phép chúng ta thực thi đoạn code nào đó nhiều lần mà khơng cần phải copy lại code, mà chỉ cần gọi tên hàm.
2.5.1 Khai báo hàm
Cú pháp
<Quyền truy cập> <Kiểu trả về> Tên hàm (<Tham số>) {
// Thân hàm // Giá trị trả về; }
Trong đó:
Tên hàm: là một tên duy nhất đƣợc sử dụng để gọi hàm.
Kiểu trả về: đƣợc sử dụng để chỉ rõ kiểu dữ liệu của hàm đƣợc trả về.
Thân hàm: là khối lệnh sẽ đƣợc thực thi khi hàm đƣợc gọi.
Quyền truy cập: đƣợc sử dụng để xác định khả năng truy cập hàm trong ứng dụng.
Tham số: là một danh sách các tham số mà chúng ta truyền vào khi gọi hàm
Ví dụ:
Chƣơng 2 – Ngơn ngữ lập trình c#
57 Khai báo hàm tham số kiểu tham trị: kiểm tra số nguyên tố
public static bool SoNT(int so) {
int u = 2;
for (; u < so; u++)
if (so % u == 0)
return false;
return true;
}
Khai báo hàm tham số kiểu tham biến ref: hàm đổi chỗ 2 số public static void Swap(ref int a, ref int b) {
a = a + b; b = a - b; a = a - b;
}
Khai báo hàm tham số kiểu tham chiếu out: hàm giải phƣơng trình bậc 2 public static bool GiaiPTB2(int a, int b, int c, out Double X1, out Double X2)
Chƣơng 2 – Ngơn ngữ lập trình c# 58 int Delta = b * b - 4 * a * c; X1 = X2 = 0; if (Delta >= 0) { X1 = (-b - Math.Sqrt(Delta)) / (2 * a); X2 = (-b + Math.Sqrt(Delta)) / (2 * a); return true; } else return false; } 2.5.2 Cách gọi hàm
Trong c# có 3 cách gọi hàm đó là gọi bằng giá trị (call by value), gọi bằng tham chiếu (call by Reference) và dùng tham số out.
Gọi bằng giá trị (call by value)
Trong C#, gọi bằng giá trị tức là tham số truyền vào là bản sao của giá trị gốc, vì vậy dù cho bên trong thân hàm có thay đổi giá trị của tham số truyền vào thì sau khi kết thúc gọi hàm thì giá trị gốc vẫn khơng thay đổi.
Chƣơng 2 – Ngơn ngữ lập trình c#
59 Trong ví dụ sau, chúng ta truyền tham số giá trị khi gọi hàm:
Gọi bằng tham chiếu (call by Reference)
C # cung cấp một từ khóa ref để truyền đối số dƣới dạng tham chiếu. Tức là tham số truyền vào bằng địa chỉ ô nhớ của biến gốc vì vậy bên trong thân hàm thay đổi giá trị tham số truyền vào thì giá trị gốc cũng thay đổi theo..
2.5.3 Truyền tham số
C # cung cấp một từ khóa ref để truyền đối số dƣới dạng tham chiếu. Tức là tham số truyền vào bằng địa chỉ ơ nhớ của biến gốc vì vậy bên trong thân hàm thay đổi giá trị tham số truyền vào thì giá trị gốc cũng thay đổi theo..
Tham số out
Tham số out giống nhƣ kiểu tham chiếu, ngoại trừ việc nó khơng u cầu biến khởi tạo trƣớc khi truyền.
Nhƣ vậy mình chỉ cần phân biệt ref và out nhƣ sau:
ref
Giá trị phải đƣợc khởi tạo trƣớc
Bên trong thân hàm có thể đọc vào thay đổi giá trị nó
out
Giá trị khơng đƣợc khởi tạo trƣớc và bên trong thân hàm khơng đọc đƣợc nó cho đến khi nó đƣợc gán giá trị
Chƣơng 2 – Ngơn ngữ lập trình c#
60
2.6 Exception và cấu trúc try..catch
2.6.1 Exception
Ngôn ngữ C# cũng giống nhƣ bất cứ ngôn ngữ hƣớng đối tƣợng khác, cho phép xử lý những lỗi và các điều kiện khơng bình thƣờng với những ngoại lệ. Ngoại lệ là một đối tƣợng đóng gói những thơng tin về sự cố của một chƣơng trình khơng bình thƣờng.
Một điều quan trọng để phân chia giữa bug, lỗi, và ngoại lệ. Một bug là một lỗi lập trình có thể đƣợc sửa chữa trƣớc khi mã nguồn đƣợc chuyển giao. Những ngoại lệ thì khơng đƣợc bảo vệ và tƣơng phản với những bug. Mặc dù một bug có thể là nguyên nhân sinh ra ngoại lệ, chúng ta cũng không dựa vào những ngoại lệ để xử lý những bug trong chƣơng trình, tốt hơn là chúng ta nên sửa chữa những bug này. Một lỗi có ngun nhân là do phía hành động của ngƣời sử dụng. Ví dụ, ngƣời sử dụng nhập vào một số nhƣng họ lại nhập vào ký tự chữ cái. Một lần nữa, lỗi có thể làm xuất hiện ngoại lệ, nhƣng chúng ta có thể ngăn ngừa điều này bằng cách bắt giữ lỗi với mã hợp lệ.
Những lỗi có thể đƣợc đốn trƣớc và đƣợc ngăn ngừa. Thậm chí nếu chúng ta xóa tất cả những bug và dự đoán tất cả các lỗi của ngƣời dùng, chúng ta cũng có thể gặp phải những vấn đề không mong đợi, nhƣ là xuất hiện trạng thái thiếu bộ nhớ (out of memory), thiếu tài nguyên hệ thống,... những nguyên nhân này có thể do các chƣơng trành khác cùng hoạt động ảnh hƣởng đến. Chúng ta không thể ngăn ngừa các ngoại lệ này, nhƣng chúng ta có thể xử lý chúng để chúng khơng thể làm tổn hại đến chƣơng trình.
Chƣơng 2 – Ngơn ngữ lập trình c#
61 Khi một chƣơng trình gặp một tình huống ngoại lệ, nhƣ là thiếu bộ nhớ thì nó sẽ tạo một ngoại lệ. Khi một ngoại lệ đƣợc tạo ra, việc thực thi của các chức năng hiện hành sẽ bị treo cho đến khi nào việc xử lý ngoại lệ tƣơng ứng đƣợc tìm thấy.
Các lớp ngoại lệ
Tên ngoại lệ Mô tả
MethodAccessException Lỗi truy cập, do truy cập đến thành viên hay phƣơng thức không đƣợc truy cập ArgumentException Lỗi tham số đối mục
ArgumentNullException Đối số Null, phƣơng thức đƣợc truyền đối số null không đƣợc chấp nhận
ArithmeticException Lỗi liên quan đến các phép toán ArrayTypeMismatchException Kiểu mảng không hợp, khi cố lƣu trữ
kiểu khơng thích hợp vào mảng DivideByZeroException Lỗi chia zero
FormatException Định dạng khơng chính xác một đối mục nào đó
IndexOutOfRangeException Chỉ số truy cập mảng không hợp lệ, dùng nhỏ hơn chỉ số nhỏ nhất hay lớn hơn chỉ số lớn nhất của mảng
Chƣơng 2 – Ngơn ngữ lập trình c#
62 MulticastNotSupportedException Multicast không đƣợc hỗ trợ, do việc kết
hợp hai delegate không đúng
NotFiniteNumberException Không phải số hữu hạn, số không hợp lệ NotSupportedException Phƣơng thức không hỗ trợ, khi gọi một phƣơng thức không tồn tại bên trong lớp.
NullReferenceException Tham chiếu null không hợp lệ. OutOfMemoryException Out of memory
OverflowException Lỗi tràn phép toán StackOverflowException Tràn stack
TypeInitializationException Kiểu khởi tạo sai, khi bộ khởi dựng tĩnh có lỗi
2.6.2 Cấu trúc try…catch Cú pháp cấu trúc try…catch
try
{
Chƣơng 2 – Ngơn ngữ lập trình c#
63 }
catch (TypeException1 ex)
{
//Mã đựơc thực thi khi một ngoại lệ TypeException1 đựơc phát sinh trong khối try
}
catch (TypeException2 ex)
{
//Mã đựơc thực thi khi một ngoại lệ TypeException2 đựơc phát sinh trong khối try
}
catch (TypeExceptionN ex)
{
//Mã đựơc thực thi khi một ngoại lệ TypeExceptionN đựơc phát sinh trong khối try
}
[finally
{
Chƣơng 2 – Ngôn ngữ lập trình c#
64 có xảy ra trong khối try hay khơng.
}]
Nếu khơng có một ngoại lệ nào phát sinh trong khối try thì các mệnh đề catch sẽ bị bỏ qua, trong trƣờng hợp một trong các câu lệnh bên trong khối try gây ra một
ngoại lệ thì, thì C# sẽ bỏ qua các câu lệnh cịn lại trong khối try để đi tìm mã xử lý ngoại lệ, nếu kiểu ngoại lệ so khớp với kiểu ngoại lệ trong mệnh đề catch, thì mã lệnh trong khối catch đó sẽ đƣợc thực thi, nếu khơng tìm thấy một kiểu ngoại lệ
nào đƣợc so khớp C# sẽ kết thúc phƣơng thức đó và chuyển biệt lệ đó ra phƣơng thức đã gọi phƣơng thức này quá trình này đƣợc tiếp tục cho đến khi tìm thấy mã xử lý biệt lệ, nếu khơng tìm thấy mã xử lý biệt lệ trong chuỗi các phƣơng thức gọi nhau, chƣơng trình có thể chấm dứt và in thơng báo lỗi…
Khối finally là tuỳ chọn, khơng bắt buộc phải có. Khối này đƣợc đặt sau khối catch cuối cùng. Chƣơng trình sẽ thực thi câu lệnh đầu tiên của khối finally ngay sau khi gặp câu lệnh return hay lệnh break trong khối try.
Khối finally bảo đảm lúc nào cũng đƣợc thực thi, bất chấp có ngoại lệ xảy ra hay khơng Ví dụ 1: int[] m = { 1, 4, 6, 7 }; try { int a, b; a = 1;
Chƣơng 2 – Ngơn ngữ lập trình c#
65 b = 0;
int c = a / b;//Co khả năng sinh ra lỗi nếu mẫu số =0
Console.WriteLine("c={0}", c);
Console.WriteLine("m{0}={1}", c, m[7]);
//m[c] có khả năng sinh ra lỗi nếu vượt ra ngồi chỉ sơ của mảng
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
Chƣơng 2 – Ngôn ngữ lập trình c#
66 Console.WriteLine(ex.Message);
}
Kết quả
attempted to divide by zero
Ví dụ 2: int[] m = { 1, 4, 6, 7 }; try { int a, b; a = 1; b = 2;
int c = a / b;//Co khả năng sinh ra lỗi nếu mẫu số =0
Console.WriteLine("c={0}", c);
Console.WriteLine("m{0}={1}", c, m[7]);
//m[c] có khả năng sinh ra lỗi nếu vượt ra ngồi chỉ sơ của mảng
Chƣơng 2 – Ngơn ngữ lập trình c#
67 catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Kết quả
Index was outside the bounds of the array
Ví dụ 3:
int[] m = { 1, 4, 6, 7 };
Chƣơng 2 – Ngơn ngữ lập trình c# 68 { int a, b; a = 1; b = 2;
int c = a / b;//Co khả năng sinh ra lỗi nếu mẫu số =0
Console.WriteLine("c={0}", c);
Console.WriteLine("m{0}={1}", c, m[7]);
//m[c] có khả năng sinh ra lỗi nếu vượt ra ngồi chỉ sơ của mảng
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
catch (IndexOutOfRangeException ex)
{
Chƣơng 2 – Ngơn ngữ lập trình c#
69 }
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.WriteLine("Vi du ve cau truc try...catch...finally");
}
Kết quả
Index was outside the bounds of the array
Chƣơng 3 – Lập trình hƣớng đối tƣợng
70
CHƢƠNG 3 – LẬP TRÌNH HƢỚNG ĐỐI TƢỢNG Mục tiêu
- Trình bày đƣợc các tính chất của lập trình hƣớng đối tƣợng. - Trình bày đƣợc các thành phần trong mơ hình hƣớng đối tƣợng.
- Sử dụng thành thạo các lớp đối tƣợng và các thành phần của lớp đối tƣợng. - Rèn luyện tính tích cực, chủ động, thích học hỏi, tƣ duy trong lập lập. - Thao tác cẩn thận an tồn trên máy tính.
3.1 Giới thiệu
Lập trình hƣớng đối tƣợng là kiểu lập trình nhằm vào sự tƣơng tác giữa các đối tƣợng. Mỗi đối tƣợng có những thuộc tính xác định các đặc điểm, những phƣơng thức xác định các chức năng của đối tƣợng. các đối tƣợng cũng có khả năng phát sinh các sự kiện khi thay đổi thuộc tính, thực hiện một phƣợng thức hay bị đối tƣợng khác tác động vào.
3.2 Các tính chất của lập trình hƣớng đối tƣợng
3.2.1 Tính trừu tƣợng
Tính trừu tƣợng là một tiến trình ẩn các chi tiết trình triển khai và chỉ hiển thị tính năng tới ngƣời dùng. Tính trừu tƣợng cho phép bạn loại bỏ tính chất phức tạp của đối tƣợng bằng cách chỉ đƣa ra các thuộc tính và phƣơng thức cần thiết của đối tƣợng trong lập trình.
Chƣơng 3 – Lập trình hƣớng đối tƣợng
71 Tính trừu tƣợng giúp bạn tập trung vào những cốt lõi cần thiết của đối tƣợng thay vì quan tâm đến cách nó thực hiện.
3.2.2 Tính đóng gói
Tức là trạng thái của đối tƣợng đƣợc bảo vệ không cho các truy cập từ code bên ngoài nhƣ thay đổi trong thái hay nhìn trực tiếp. Việc cho phép môi trƣờng bên ngoài tác động lên các dữ liệu nội tại của một đối tƣợng theo cách nào là hoàn toàn tùy thuộc vào ngƣời viết mã. Đây là tính chất đảm bảo sự tồn vẹn, bảo mật của đối tƣợng Trong Java, tính đóng gói đƣợc thể hiện thơng qua phạm vi truy cập (access modifier). Ngoài ra, các lớp liên quan đến nhau có thể đƣợc gom chung lại thành package.
3.2.3 Tính thừa kế
Tính kế thừa là khả năng cho phép ta xây dựng một lớp mới dựa trên các định nghĩa của một lớp đã có. Lớp đã có gọi là lớp Cha, lớp mới phát sinh gọi là lớp Con