chuong 2
NGÔN NGỮ C#
Đặng Thành Trung
GIỚI THIỆU
Được phát triển bởi đội ngũ kỹ sư của Microsoft, dẫn đầu là Anders Hejlsberg và Scott Wiltamuth.
Có khoảng 80 từ khóa và 20 kiểu dữ liệu dựng sẵn
C# là ngôn ngữ lập trình hướng đối tượng và có những từ khóa hỗ trợ đầy đủ việc khai báo một lớp
C# có các đặc trưng
Hỗ trợ khái niệm giao diện
Hỗ trợ kiểu cấu trúc
Cung cấp đặc trưng hướng thành phần : property, sự kiện,
Truy cập bộ nhớ trực tiếp dùng con trỏ
TỪ KHÓA C#
CHƯƠNG TRÌNH ĐẦU TIÊN
Chương trình bắt đầu thực hiện từ hàm Main
In ra màn hình
Tên file và class không nhất thiết giống nhau
CẤU TRÚC CHƯƠNG TRÌNH
Nếu namespace không được xác định => sử dụng namespace mặc định
Namespace có thể chứa struct, interface, delegate và enum
Namespace có thể được sử dụng ở các file khác
Trường hợp đơn giản nhất: Một lớp, một file, và chỉ sử dụng namespace mặc định
MỘT CHƯƠNG TRÌNH 2 FILE
CÁC BƯỚC TẠO CHƯƠNG TRÌNH
Xác định mục tiêu chương trình
Xác định phương pháp giải quyết
Tạo chương trình giải quyết
Thực thi chương trình để xem kết quả
LỚP, ĐỐI TƯỢNG, KIỂU
Bản chất của lập trình hướng đối tượng là tạo ra kiểu mới
Một kiểu mới được định nghĩa bằng từ khóa class gọi là một lớp.
Khai báo lớp trong C# không có dấu ";" ở cuối lệnh khai báo.
Khai báo lớp trong C# không chia thành 2 phần header và definition như trong C++.
Một thể hiện của lớp gọi là một đối tượng
PHƯƠNG THỨC
Hai thành phần chính của lớp là
Phương thức (Method) : các hàm được định nghĩa trong lớp
Thuộc tính (Attribute) : các biến được khái báo trong lớp
Hàm "Main" là một phương thức đặc biệt của lớp, xác định đầu vào của lớp.
Giá trị của hàm Main có thể là void hoặc int.
Mỗi chương trình có thể có nhiều hàm Main nhưng cần phải xác định chỉ dẫn biên dịch để thực thi một hàm Main cụ thể.
CHÚ THÍCH
Chú thích nhằm mục đích giải thích các đoạn mã khác
Chú thích là các đoạn mã chương trình không được biên dịch
Có hai loại chú thích
Chú thích đơn dòng, bắt đầu bằng ký tự "//"
Chú thích đa dòng, bắt đầu bằng ký tự "/*" và kết thúc bằng ký tự "*/"
ỨNG DỤNG CONSOLE
Ứng dụng giao tiếp với người dùng thông qua bàn phím và không có giao diện đồ họa.
NAMESPACE
.NET có rất nhiều các lớp thư viện khác nhau.
Các lớp được nhóm với nhau bằng các vùng tên (name space).
Các vùng tên cũng có thể được nhóm với nhau bằng các vùng tên lớn hơn.
Người dùng có thể tạo ra các vùng tên riêng cho các lớp tự định nghĩa.
Để truy xuất đến các phương thức trong các lớp, trong các vùng tên, ... người ta sử dụng toán tử "."
Vùng_tên.Vùng_tên_con....Tên_lớp...Tên_phương_thức
PHÂN BIỆT HOA THƯỜNG
Ngôn ngữ C# phân biệt chữ hoa, chữ thường giống như C, C++
Ví dụ : Writeline, WriteLine, WRITELINE, ... là khác nhau
Tên biến, hàm, hằng, lớp, ... đều phân biệt chữ hoa, chữ thường
KIỂU DỮ LIỆU & TOÁN TỬ
Đặng Thành Trung
PHÂN LOẠI KIỂU
KIỂU GIÁ TRỊ & KIỂU THAM CHIẾU
CÁC KIỂU DỰNG SẴN
CHUYỂN ĐỔI KIỂU
Một đối tượng có thể chuyển đổi từ kiểu này sang kiểu khác theo hai cách:
Chuyển đổi ngầm định: được thực hiện tự động bởi trình biên dịch.
Chuyển đổi tường minh: có sự can thiệp của người lập trình.
KIỂU LIỆT KÊ : enum
Được sử dụng để liệt kê các hằng
TOÁN TỬ KIỂU ENUM
Chương trình không kiểm tra kết quả có phải là một giá trị hợp lệ của kiểu enum không
Chú ý:
enum không được gán cho kiểu int, trừ khi phải ép kiểu tường minh
enum kế thừa kiểu object (Equals, ToString, ...)
Lớp System.enum cung cấp các phương thức : GetName, Format, GetValues,...
MỘT SỐ KIỂU MỞ RỘNG
Kiểu xâu (string) : được sử dụng như một kiểu chuẩn string.
Khai báo : string s ="Hello";
Kiểu string không thể chỉnh sửa được (Nếu muốn chỉnh sửa dùng StringBuilder)
Toán tử nối chuỗi là phép cộng "+"
string là kiểu tham chiếu, nhưng có thể thực hiện phép so sánh
Lớp string có nhiều phương thức hữu dụng: Length, CompareTo, IndexOf, StartsWith, Substring...
MỘT SỐ KIỂU MỞ RỘNG
Mảng : tập hợp các phần tử có cùng kiểu
Khai báo : int [] a; int [][] b;
Mỗi thành phần được xác định thông qua chỉ số
Có nhiều kiểu mảng : mảng một chiều, mảng nhiều chiều, mảng ziczắc, ...
Danh sách : mảng có kích thước thay đổi.
Khai báo : ArrayList a;
Cấu trúc (struct)
Lớp (class)
BIẾN VÀ HẰNG
Biến là một vùng nhớ dành riêng cho một kiểu dữ liệu nào đó.
Biến có thể được gán giá trị và có thể thay đổi giá trị trong quá trình thực hiện chương trình.
Biến phải luôn được gán giá trị trước khi được sử dụng.
Cú pháp khai báo biến
<tên kiểu> <tên biến>
Ví dụ : int x,y;
Hằng là một biến nhưng giá trị của hằng không thể thay đổi trong quá trình thực hiện chương trình.
Cú pháp khai báo hằng
<const> <tên kiểu> <tên hằng> = <giá trị>
Ví dụ : const float so_pi = 3.1415
TOÁN TỬ
Toán tử được ký hiệu bằng một biểu tượng, dùng để thực hiện một hành động
Toán tử gán: được ký hiệu là dấu "=", là toán tử hai ngôi làm toán hạng bên trái có giá trị bằng toán hạng bên phải.
Ví dụ : x = y;
Toán tử toán học : Là các toán tử thao tác số học.
Có 5 toán tử toán học : +,-,*,/ và %
Toán tử tăng, giảm: Thực hiện các thao tác số học và gán lại giá trị cho chính biến được tính toán
Có 5 toán tử tăng, giảm tương ứng với 5 toán tử số học: +=, -=, *=, /=, %=
Ví dụ : x = x + 3 tương đương với x += 3;
TOÁN TỬ
Toán tử tăng, giảm 1: ++, --
Ví dụ: x = x + 1 tương đương với x+=1 hoặc x++
Có hai loại tăng, giảm 1 :
Tăng, giảm tiền tố : ++x hoặc --x
Tăng, giảm hậu tố : x++ hoặc x-
Toán tử quan hệ : Dùng để so sánh giữa hai giá trị và trả về một giá trị logic kiểu bool (true hoặc false).
== : so sánh bằng
!= : so sánh khác
> : so sánh lớn hơn
>= : so sánh lớn hơn hoặc bằng
< : so sánh nhỏ hơn
<= : so sánh nhỏ hơn hoặc bằng
TOÁN TỬ
Toán tử logic : Dùng để kết hợp hai hoặc nhiều biểu thức logic
&& : kết hợp theo phép and
|| : kết hợp theo phép or
! : kết hợp theo phép not.
Toán tử thao tác bit : Dùng để thực hiện các phép toán nhị phân
& : phép and bit
| : phép or bit
~ : phép not bit
^ : phép xor bit
Toán tử ba ngôi :
Cú pháp : <biểu thức điều kiện>?<biểu thức 1>:<biểu thức 2>
Nếu biểu thức điều kiện đúng sẽ trả về giá trị của biểu thức 1, ngược lại trả về giá trị của biểu thức 2.
ĐỘ ƯU TIÊN TOÁN TỬ
KHAI BÁO
Đặng Thành Trung
PHẠM VI KHÁI BÁO
Phân loại phạm vi khai báo
namespace: khai báo class, interface, struct, enum, delegate
class, interface, struct: khai báo trường dữ liệu, phương thức, ...
enumeration: khai báo các hằng thuộc enum
khối lệnh: khai báo các biến cục bộ
CÁC NGUYÊN TẮC KHAI BÁO
Nguyên tắc về phạm vi khai báo
Không có hai tên trùng nhau trong cùng một phạm vi và cùng một mức
Có thể khai báo trùng tên khi ở các mức khác nhau
Nguyên tắc về tầm hoạt động
Tên chỉ có tác dụng trong phạm vi khai báo của nó.
Tầm hoạt động còn bị ảnh hưởng bởi các modifier như: public, private, protected và internal.
VÍ DỤ
Các namespace ở file khác nhau tạo lên phạm vi khai báo khác nhau
Namespace lồng tạo nên phạm vi khai báo của riêng nó
SỬ DỤNG NAMESPACE
Đối với các namespace ngoài
Phải bổ sung thêm, dùng từ khoá using (e.g. using Util;)
sử dụng tên đầy đủ (e.g. Util.Color)
Hầu hết các chương trình phải sử dụng namespace System => using System;
CLASS, INTERFACE, STRUCT
Phạm vi khai báo của subclass không phụ thuộc vào phạm vi khai báo của base class => có thể khai báo tên trùng nhau trong subclass và base class
KHỐI LỆNH
Phạm vi khai báo của khối lệnh bao gồm phạm vi khai báo của các khối lệnh lồng trong nó.
Các tham số hình thức phụ thuộc vào phạm vi khai báo của khối lệnh của phương thức.
Các biến được sử dụng trong vòng lặp phụ thuộc vào khối lệnh của vòng lặp
Phải khai báo biến cục bộ trước khi sử dụng chúng
KHAI BÁO BIẾN CỤC BỘ
CÂU LỆNH
Đặng Thành Trung
CÁC CÂU LỆNH ĐƠN GIẢN
Lệnh rống
; // dấu "; " là kết thúc lệnh
Lệnh gán
x = 3 * y + 1;
Gọi phương thức
string s = "a,b,c";
string[] parts = s.Split(','); // viện dẫn tới phương thức của đối tượng
// (non-static)
s = String.Join(" + ", parts); // viện dẫn tới phương thức của class (static)
LỆNH IF ... ELSE ...
Lệnh if ... else ... phân nhánh dựa trên một điều kiện duy nhất.
else là phần tùy chọn, và chỉ được thực hiện khi điều kiện khi điều kiện bị sai
Cú pháp :
if (biểu thức điều kiện)
<khối lệnh 1>
[else <khối lệnh 2>]
Nếu các khối lệnh trong điều kiện if có nhiều hơn một lệnh thì chúng phải được đặt trong cặp dấu {}
LỆNH SWITCH
Lệnh switch phân nhánh tùy theo từng giá trị của của biểu thức điều kiện
Cú pháp :
switch (biểu thức điều kiện) {
case <giá trị 1> :
<khối lệnh 1>
<lệnh nhảy>
case <giá trị 2> :
<khối lệnh 2>
<lệnh nhảy>
...
[default :
<khối lệnh default>]
}
default là phần tùy chọn được thực hiện khi biểu thức điều kiện không khớp với phần case nào.
Lệnh nhảy có thể là break, goto, ...
VÍ DỤ
Cài đặt switch với lệnh nhảy break
switch (country) {
case "England":
case "USA":
language = "English";
break;
case "Germany":
case "Austria":
case "Switzerland":
language = "German";
break;
case null:
Console.WriteLine("no country specified");
break;
default:
Console.WriteLine("don't know the language of " + country);
break;
}
LỆNH LẶP
Lệnh while :
Ý nghĩa : Trong khi điều kiện đúng thì làm ...
Cú pháp :
while (biểu thức điều kiện)
<khối lệnh>
Biểu thức điều kiện chỉ có giá trị đúng hoặc sai
Vòng lặp while luôn kiểm tra điều kiện trước khi thực hiện khối lệnh.
Nếu khối lệnh có nhiều hơn một lệnh thì các lệnh phải được đặt trong dặp dấu {}
LỆNH LẶP
Lệnh do ... while :
Ý nghĩa : Làm trong khi điều kiện đúng
Cú pháp :
do
<khối lệnh>
while (biểu thức điều kiện)
Biểu thức điều kiện chỉ có giá trị đúng hoặc sai
Vòng lặp while luôn kiểm tra điều kiện trước sau khi thực hiện khối lệnh.
Nếu khối lệnh có nhiều hơn một lệnh thì các lệnh phải được đặt trong dặp dấu {}
LỆNH LẶP
Lệnh for :
Gồm ba phần chính
Khởi tạo biến đếm vòng lặp
Kiểm tra biến đếm, nếu đúng thì thực hiện khối lệnh trong vòng lặp
Thay đổi biến đếm
Cú pháp :
for ([phần khởi tạo];[biểu thức điều kiện];[thay đổi bước lắp])
<khối lệnh>
Ví dụ :
for (int i = 0; i<n; i++)
sum += i;
Nếu khối lệnh có nhiều hơn một lệnh thì các lệnh phải được đặt trong dặp dấu {}
LỆNH LẶP
Lệnh foreach : Lặp trên một tập hợp hoặc một mảng
Cú pháp
foreach (<kiểu tập hợp> <tên thành phần> in <tên tập hợp>)
<khối lệnh>
Ý nghĩa : Vòng lặp sẽ duyệt qua từng phần tử của toàn bộ tập hợp hoặc mảng.
Ví dụ :
int[] a = {3, 17, 4, 8, 2, 29};
foreach (int x in a) sum += x;
LỆNH NHẢY
break : Dùng để "nhảy" ra khỏi lệnh lặp hoặc lệnh case.
continue : Bỏ qua phần còn lại và tiếp tục vòng lặp tiếp theo.
goto : Nhảy tới một nhãn xác định.
Không nhảy vào một khối lệnh
Không nhảy ra ngoài khối lệnh finally
LỆNH ĐƠN GIẢN TRÊN CONSOLE
Hiển thị lên Console
Cú pháp :
System.Console.Write(<tham số>)
System.Console.WriteLine(<tham số>)
Ví dụ :
System.Console.Write("Hello everybody.");
System.Console.Write("x = {0}",x);
Đọc giá trị từ bàn phím
Cú pháp :
int System.Console.Read();
string System.Console.ReadLine();
Ví dụ :
int ch = System.Console.Read();
string line = System.Console.ReadLine();
MẢNG
ĐẶNG THÀNH TRUNG
MẢNG
Mảng là một tập hợp có thứ tự của những đối tượng có cùng kiểu.
Các mảng khai báo trong C# thực chất là đối tượng của kiểu System.Array
Cú pháp khai báo mảng:
<kiểu dữ liệu>[] <tên mảng>
<kiểu dữ liệu>[,] <tên mảng>
Ví dụ :
int[] myArray;
Tạo thể hiện mảng:
myArray = new int[6];
TRUY CẬP MẢNG
Để truy cập vào mảng, dùng toán tử chỉ mục []
Chỉ mục đầu tiên của mảng bắt đầu từ 0.
Khi tạo một mảng có kiểu giữ liệu giá trị, mỗi thành phần sẽ chứa giá trị mặc định của kiểu dữ liệu.
Mảng các số nguyên sẽ có giá trị ban đầu là 0
Mảng các kiểu tham chiếu sẽ có giá trị ban đầu là null
Để truyền kiểu mảng cho các phương thức, phải dùng từ khóa params
Ví dụ : public void DisplayVals(params int[] val);
CÁC LOẠI MẢNG
Mảng 1 chiều:
int[] a = new int[3];
int[] b = new int[] {3, 4, 5};
int[] c = {3, 4, 5};
SomeClass[] d = new SomeClass[10];// phần tử của mảng là tham chiếu
SomeStruct[] e = new SomeStruct[10]; // phần tử của mảng là giá trị
Mảng nhiều chiều
Mảng nhiều chiều hình chữ nhật
int[,] a = new int[2, 3];
int[,] b = {{1, 2, 3}, {4, 5, 6}};
int[,,] c = new int[2, 4, 2];
Mảng nhiều chiều zíc zắc
int[][] a = new int[2][]; // Phần tử của mảng tham chiếu tới một mảng khác
a[0] = new int[] {1, 2, 3}; // Khởi tạo từng phần tử của mảng
a[1] = new int[] {4, 5, 6};
CÁC LOẠI MẢNG
CÁC PHƯƠNG THỨC MẢNG
CÁC PHƯƠNG THỨC MẢNG
LỚP VÀ CẤU TRÚC
Đặng Thành Trung
LỚP
Khai báo lớp
[Thuộc tính] [Bổ sung truy cập] class <Định danh lớp> [: Lớp cơ sở]
{
<Phần thân của lớp: bao gồm định nghĩa các thuộc tính và
phương thức hành động >
}
Trong C#, phần kết thúc của lớp không có dấu ":" giống như trong C++. Tuy nhiên nếu người lập trình đưa vào thì chương trình dịch vẫn không báo lỗi.
Trong C#, mọi chuyện đều xảy ra trong một lớp.
Lớp chỉ kế thừa từ một lớp khác
Lớp có thể cài đặt cho nhiều interface.
Một đối tượng gọi là một thể hiện của lớp.
Một đối tượng được cấp phát vùng nhớ trên heap
Đối tượng phải được tạo với từ khóa new
VÍ DỤ VỀ LỚP
class Thoigian {
int nam, thang, ngay; //các thuộc tính
int gio, phut, giay;
public thoiGianHienHanh() { //một phương thức
System.Console.WriteLine("Hien thi thoi gian !");
}
}
public class Tester {
static void Main() {
Thoigian t = new Thoigian();
t.thoiGianHienHanh();
}
}
THUỘC TÍNH TRUY CẬP
Thuộc tính truy cập quyết định khả năng truy cập vào lớp từ các yếu tố bên ngoài
Trong C#,có các thuộc tính truy cập như sau:
public : Không hạn chế truy cập
private : Chỉ các thành phần bên trong lớp mới được truy cập.
protect : Chỉ các thành phần bên trong lớp hoặc lớp kế thừa có thể truy cập.
internal : Chỉ các thành phần của lớp cùng khối khai báo có thể truy cập
protected internal : chỉ các thành phần của lớp cùng khối khai báo hoặc kế thừa có thể truy cập.
Khi không khai báo rõ thuộc tính truy cập, các thành phần sẽ được xem như là private.
THUỘC TÍNH
THUỘC TÍNH TĨNH
Phụ thuộc vào class, không phụ thuộc vào đối tượng
class Rectangle {
static Color defaultColor; // mỗi class chỉ có một
static readonly int scale; // mỗi class chỉ có một
int x, y, width,height; // mỗi đối tượng có một
...
}
Hằng không được khai báo với từ khoá static
Truy nhập trong class Truy nhập từ class khác
... defaultColor ... scale ... ... Rectangle.defaultColor ... Rectangle.scale .
ĐÓNG GÓI DỮ LIỆU
Thực hiện truy cập vào các biến thành viên của lớp thông qua các phương thức nhằm bảo mật dữ liệu của lớp
Có hai bộ truy cập được thiết lập
Bộ truy cập lấy dữ liệu
Bộ truy cập thiết lập dữ liệu
Ví dụ
ĐÓNG GÓI DỮ LIỆU
Đóng gói dữ liệu giúp người lập trình kiểm tra trường dữ liệu trước khi sử dụng
Đóng gói dữ liệu được sử dụng để thiết lập quyền readonly hoặc writeonly cho thuộc tính.
PHƯƠNG THỨC
Ví dụ
class C {
int sum = 0, n = 0;
public void Add (int x) {
sum = sum + x; n++;
}
public float Mean() {
return (float)sum / n;
}
}
Truy nhập trong class Truy nhập từ class khác
Add(3); C c = new C();
float x = Mean(); c.Add(3);
float x = c.Mean();
PHƯƠNG THỨC TĨNH
Chỉ làm việc trên các thành phần dữ liệu static
class Rectangle {
static Color defaultColor;
public static void ResetColor() {
defaultColor = Color.white;
}
}
Truy nhập trong class Truy nhập từ class khác
ResetColor(); Rectangle.ResetColor();
THAM SỐ PHƯƠNG THỨC
Một phương thức có thể có số lượng tham số tùy ý.
Mỗi tham số phải được khai báo cùng kiểu dữ liệu
Các tham số được xem như các biến cục bộ
Có ba kiểu tham số :
THAM SỐ PHƯƠNG THỨC
THAM SỐ PHƯƠNG THỨC
Tham số có số lượng thay đổi
NẠP CHỒNG PHƯƠNG THỨC
Nạp chồng phương thức là cách khai báo các phương thức có thể trùng tên nhau.
Các phương thức có thể khai báo trùng tên nhau nếu
Số lượng tham số của chúng khác nhau
Kiểu dữ liệu của các tham số là khác nhau
Kiểu truyền tham số khác nhau
NẠP CHỒNG PHƯƠNG THỨC
Các hàm chồng chỉ khác nhau về kiểu dữ liệu trả về không được chấp nhận
Khai báo :
int F();
string F();
Gọi hàm
F(); //lỗi khi dịch
Các hàm chồng sau cũng không hợp lệ
Khai báo
void P(int[] a) {...}
void P(params int[] a) {...}
Gọi hàm
int[] a = {1, 2, 3};
P(a);
P(1, 2, 3);
HÀM KHỞI TẠO CỦA LỚP
Phương thức được thực hiện khi người dùng tạo ra một đối tượng gọi là hàm khởi tạo của lớp
Hàm khởi tạo cũng bị nạp chồng
Trong hàm khởi tạo có thể gọi hàm khởi tạo khác, sử dụng từ khóa this
HÀM KHỞI TẠO MẶC ĐỊNH
Nếu người dùng không định nghĩa hàm khởi tạo khi xây dựng lớp thì hệ thống sử dụng hàm khởi tạo mặc định, không có tham số.
class C { int x; }
C c = new C(); // ok
Hàm tạo mặc định khởi tạo trường dữ liệu theo nguyên tắc sau:
số 0
enum 0
bool false
char '\0'
tham chiếu null
Nếu class khai báo hàm tạo, thì hàm tạo mặc định sẽ không được tạo ra
class C {
int x;
public C(int y) { x = y; }
}
C c1 = new C(); // lỗi khi dịch chương trình
C c2 = new C(3); // ok
HÀM KHỞI TẠO TĨNH
Không có tham số và không sử dụng từ khoá public hoặc private
Mỗi class/struct chỉ có một hàm tạo static
Nó chỉ được viện dẫn một lần trước khi class/struct được sử dụng.
Được sử dụng để khởi tạo các trường dữ liệu static
HÀM KHỞI TẠO SAO CHÉP
Hàm khởi tạo sao chép thực hiện sao chép tất cả các biến từ một đối tượng đã có và cùng một kiểu dữ liệu.
TỪ KHÓA this
Từ khóa this được dùng để tham chiếu đến thể hiện hiện thời của một đối tượng
Từ khóa this được xem là con trỏ ẩn đến tất cả các phương thức không có thuộc tính tĩnh trong một lớp.
Tham chiếu this được sử dụng theo ba cách
Sử dụng khi các biến thành viên bị che lấp bởi tham số đưa vào
Sử dụng tham chiếu this để truyền đối tượng hiện hành vào một tham số của một phương thức đối tượng khác
Sử dụng tham chiếu this như là mảng chỉ mục (indexer)
HÀM HỦY CỦA LỚP
Được gọi trên một đối tượng trước khi bị xoá bởi garbage collector.
Hàm huỷ của lớp cơ sở được tự động gọi vào cuối cùng
Không sử dụng từ khoá public hoặc private.
Struct không cần có hàm huỷ
Ví dụ
class Test {
~Test() {
... cleanup actions ...
}
}
INDEXER
Indexer được sử dụng để đánh chỉ số cho một tập hợp
class File {
FileStream s;
public int this [int index] {
get {s.Seek(index, SeekOrigin.Begin);
return s.ReadByte();
}
set {s.Seek(index, SeekOrigin.Begin);
s.WriteByte((byte)value);
}
}
}
Sử dụng
File f = ...;
int x = f[10]; // gọi phương thức f.get(10)
f[10] = 'A'; // gọi phương thức f.set(10, 'A')
Có thể bỏ qua phương thức get hoặc set (read-only hoặc write-only)
Indexer có thể bị chồng với nhiều kiểu chỉ số khác nhau
INDEXER
class MonthlySales {
int[] apples = new int[12];
int[] bananas = new int[12];
...
public int this[int i] {
// bỏ qua phương thức set => read-only
get { return apples[i-1] + bananas[i-1]; }
}
public int this[string month] {
// indexer bị chồng
get {
switch (month) {
case "Jan": return apples[0] + bananas[0];
case "Feb": return apples[1] + bananas[1];
...
}
}
}
}
MonthlySales sales = new MonthlySales();
Console.WriteLine(sales[1] + sales["Feb"]);
CẤU TRÚC
Là một kiểu đơn giản, có kích thước nhỏ, dùng để thay thế cho lớp.
Các đối tượng được cấp phát trên stack (struct là kiểu giá trị)
Khai báo
[thuộc tính] [bổ sung truy cập] struct <tên cấu trúc> [:<danh sách giao diện>]{
[<thành viên của cấu trúc>]
}
Ví dụ
struct Point {
int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public MoveTo (int x, int y) {...}
}
CẤU TRÚC
Cấu trúc có thể được cấp phát với từ khóa new
Point p; // các trường dữ liệu của p chưa được khởi tạo
Point q = new Point();
Các trường dữ liệu không được khởi tạo lúc khai báo
struct Point {
int x = 0; // lỗi dịch chương trình
}
Cấu trúc không hỗ trợ việc thừa kế nhưng có thể cài đặt cho giao diện
Cấu trúc không có bộ hủy và bộ khởi tạo mặc định tùy chọn
HÀM KHỞI TẠO CỦA struct
Chương trình dịch sẽ tạo ra hàm tạo mặc định, không có tham số cho tất cả các struct (kể cả struct đã khai báo hàm tạo)
Không cần phải khai báo hàm tạo không có tham số cho struct
Hàm tạo của struct phải khởi tạo tất cả các trường dữ liệu
CLASS & STRUCT
Class System.Object
Là class cơ sở của tất cả các kiểu tham chiếu
class Object {
public virtual bool Equals(object o) {...}
public virtual string ToString() {...}
public virtual int GetHashCode() {...}
...
}
Có thể được sử dụng như các kiểu chuẩn: object
object obj; // chương trình dịch sẽ ánh xạ kiểu object sang kiểu System.Object
Có thể gán đối tượng của kiểu Object cho bất kỳ kiểu tham chiếu nào
obj = new Rectangle();
obj = new int[3];
Các phương thức của Object có thể làm việc trên bất kỳ kiểu tham chiếu nào
void Push(object x) {...}
Push(new Rectangle());
Push(new int[3]);
Boxing và Unboxing
Các kiểu giá trị (int, struct, enum) tương thích với kiểu object
Boxing :
object obj = 3;
gói giá trị 3 vào một đối tượng trên heap (wrap)
Unboxing :
int x = (int) obj;
trả giá trị từ đối tượng trên heap (unwrap)
Boxing và Unboxing
class Queue {
...
public void Enqueue(object x) {...}
public object Dequeue() {...}
...
}
Queue có thể sử dụng cả kiểu tham chiếu và kiểu giá trị
Queue q = new Queue();
q.Enqueue(new Rectangle());
q.Enqueue(3);
Rectangle r = (Rectangle) q.Dequeue();
int x = (int) q.Dequeue();
NẠP CHỒNG TOÁN TỬ
Đặng Thành Trung
VÍ DỤ
Xây dựng lớp phân số có thể thực hiện các phép toán số học : cộng, trừ, nhân chia, ...
Có hai cách xây dựng các phép toán
Dùng phương thức :
Faction theSum,firstFraction, secondFraction;
the Sum = firstFraction.add(secondFraction);
Dùng toán tử
Faction theSum,firstFraction, secondFraction;
the Sum = firstFraction+secondFraction;
SỬ DỤNG TỪ KHÓA operator
Các toán tử là các phương thức tĩnh. Giá trị trả về thể hiện kết quả của một toán tử và các tham số là toán hạng.
Tạo một toán tử cho một lớp tức là thực hiện việc lạp chồng.
Toán tử nạp chồng được khai báo sử dụng từ khóa operator
Ví dụ
class Fraction {
int x, y;
public Fraction (int x, int y) {this.x = x; this.y = y; }
public static Fraction operator + (Fraction a, Fraction b) {
return new Fraction(a.x * b.y + b.x * a.y, a.y * b.y);
}
}
Sử dụng
Fraction a = new Fraction(1, 2);
Fraction b = new Fraction(3, 4);
Fraction c = a + b; // c.x == 10, c.y == 8
MỘT SỐ LUẬT
Định nghĩa những toán tử trong kiểu dữ liệu giá trị, kiểu do ngôn ngữ xây dựng sẵn
Cung cấp những phương thức nạp chồng toán tử chỉ bên trong của lớp nơi mà những phương thức được định nghĩa.
Sử dụng tên và những ký hiệu qui ước mô tả trong Common Language Specification (CLS)
Phải cung cấp các phương thức thay thế cho toán tử nạp chồng
MỘT SỐ LUẬT
MỘT SỐ LUẬT
MỘT SỐ LUẬT
TOÁN TỬ SO SÁNH BẰNG
Nếu nạp chồng toán tử so sánh bằng (==), nên phủ quyết phương thức ảo Equals của lớp object và chuyển lại cho toán tử so sánh bằng thực hiện.
Ví dụ
public overide bool Equals(object o)
{
if ( !(o is Fraction) ) return false;
return this == (Fraction) o;
}
Nạp chồng toán tử có tính đối xứng, tức là nếu toán tử so sánh bằng được nạp chồng thì cũng phải nạp chồng toán tử không bằng (!=).
Nếu toán tử (==) và (!=) mà được nạp chồng và phương thức Equals không được nạp chồng thì chương trình sẽ cảnh báo.
VÍ DỤ
using System;
public class Fraction
{
public Fraction(int numerator,int denominator)
{
Console.WriteLine("In Fraction Constructor( int, int) ");
this.numerator = numerator;
this.denominator = denominator;
}
public Fraction(int wholeNumber)
{
Console.WriLine("In Fraction Constructor( int )");
numerator = wholeNumber;
denominator = 1;
}
public static implicit operator Fraction( int theInt )
{
Console.WriteLine(" In implicit conversion to Fraction");
return new Fraction( theInt );
}
VÍ DỤ
public static explicit operator int( Fraction theFraction )
{
Console.WriteLine("In explicit conversion to int");
return theFraction.numerator / theFraction.denominator;
}
public static bool operator == ( Fraction lhs, Fraction rhs)
{
Console.WriteLine("In operator ==");
if ( lhs.numerator == rhs.numerator &&
lhs.denominator == rhs.denominator ) return true;
// thực hiện khi hai phân số không bằng nhau
return false;
}
public static bool operator != ( Fraction lhs, Fraction rhs)
{
Console.WriteLine("In operator !=");
return !( lhs == rhs );
}
public override bool Equals( object o )
{
Console.WriteLine("In method Equals");
if ( !(o is Fraction )) return false;
return this == ( Fraction ) o;
}
VÍ DỤ
public static Fraction operator+( Fraction lhs, Fraction rhs )
{
Console.WriteLine("In operator +");
if (lhs.denominator == rhs.denominator )
return new Fraction( lhs.numerator + rhs.numerator, lhs.denominator );
//thực hiện khi hai mẫu số khộng bằng nhau
int firstProduct = lhs.numerator * rhs.denominator;
int secondProduct = rhs.numerator * lhs.denominator;
return new Fraction( firstProduct + secondProduct,
lhs.denominator * rhs.denominator);
}
public override string ToString()
{
string s = numerator.ToString() + "/" + denominator.ToString();
return s;
}
//biến thành viên lưu tử số và mẫu số
private int numerator;
private int denominator;
}
VÍ DỤ
public class Tester
{
static void Main()
{
Fraction f1 = new Fraction( 3, 4);
Console.WriteLine("f1:{0}",f1.ToString());
Fraction f2 = new Fraction( 2, 4);
Console.WriteLine("f2:{0}",f2.ToString());
Fraction f3 = f1 + f2;
Console.WriteLine("f1 + f2 = f3:{0}",f3.ToString());
Fraction f4 = f3 + 5;
Console.WriteLine("f4 = f3 + 5:{0}",f4.ToString());
Fraction f5 = new Fraction( 2, 4);
if( f5 == f2 )
{
Console.WriteLine("f5:{0}==f2:{1}",
f5.ToString(), f2.ToString());
}
}
}
THỪA KẾ VÀ ĐA HÌNH
Đặng Thành Trung
ĐẶC BIỆT HÓA & TỔNG QUÁT HÓA
Lớp và các thể hiện của lớp (đối tượng) tuy không cùng tồn tại trong cùng một khối nhưng chúng tồn tại trong một mạng lưới các quan hệ phụ thuộc lẫn nhau.
Quan hệ là một (is-a) là một sự đặc biệt hóa
Mèo là một loài động vật có vú. Nó có tất cả các đặc tính của một loài động vật có vú và có thêm các thuộc tính đặc trưng riêng của họ nhà mèo.
Quan hệ đặc biệt hóa & tổng quát hóa là đối ngẫu và phân cấp
Tạo ra cây quan hệ
Di chuyển đi lên là tổng quát hóa, ngược lại di chuyển đi xuống là đặc biệt hóa.
KẾ THỪA
Quan hệ đặc biệt hóa được thực thi bằng cách sử dụng sự kế thừa.
Trong ngôn ngữ C#, để tạo một lớp kế thừa từ lớp khác, thêm dấu ":" vào sau tên lớp đó
public class Meo : Dongvatcovu
Lớp kế thừa (Meo) gọi là lớp dẫn xuất
Lớp được kế thừa (Dongvatcovu) gọi là lớp được dẫn xuất hoặc lớp cơ sở.
Lớp dẫn xuất kế thừa tất cả các thành phần của lớp cơ sở.
Lớp dẫn xuất có thể tạo phương thức mới bằng việc đánh dấu với từ khóa new
VÍ DỤ
class A { // lớp cơ sở
public int a;
public A() {...}
public void F() {...}
}
class B : A { // lớp con (kế thừa từ A, mở rộng A)
int b;
public B() {...}
public void G() {...}
}
Lớp B kế thừa thuộc tính a và phương thức F, bổ sung thêm thuộc tính b và phương thức G
Hàm tạo không được kế thừa
Các phương thức được kế thừa có thể chồng lớp con
Một lớp chỉ được kế thừa từ lớp khác nhưng có thể cài đặt cho nhiều interface.
Một lớp chỉ có thể kế thừa từ một lớp khác không phải là struct
Một lớp không có lớp cơ sở tường minh sẽ kế thừa từ lớp object
GÁN VÀ KIỂM TRA KIỂU
Khai báo
class A {...}
class B : A {...}
class C: B {...}
Lệnh gán
A a = new A(); // kiểu static của biến a là kiểu khi khai báo (A)
// kiểu dynamic type của biến a là kiểu của đối tượng được khởi tạo (A)
a = new B(); // kiểu dynamic là B
a = new C(); // kiểu dynamic là C
B b = a; // lỗi khi dịch chương trình
Kiểm tra kiểu khi thực thi (Toán tử is)
a = new C();
if (a is C) ... // true, nếu kiểu dynamic của a là C hoặc subclass của C
if (a is B) ... // true
if (a is A) ... // true
a = null;
if (a is C) ... // false
ÉP KIỂU
Ép kiểu
A a = new C();
B b = (B) a; // đúng, nếu a có kiểu dynamic là B hoặc subclass của B
C c = (C) a;
a = null;
c = (C) a; // đúng, c = null
Toán tử as
A a = new C();
B b = a as B; // if (a is B) b = (B)a; else b = null;
C c = a as C;
a = null;
c = a as C; // c == null
CHỒNG PHƯƠNG THỨC
Chỉ những phương thức được khai báo với từ khoá virtual mới được chồng.
class A {
public void F() {...} // F không được chồng
public virtual void G() {...} // Có thể chồng G trong subclass
}
Các phương thức chồng được khai báo với từ khoá override
class B : A {
public void F() {...} // nên sử dụng từ khoá new
public void G() {...} // nên sử dụng từ khoá new
public override void G() { // ok: chồng toán tử G
... base.G(); // gọi G() của lớp cơ sở
}
}
Thuộc tính và indexer cũng được chồng.
Phương thức static không được chồng
GÁN ĐỘNG
class A {
public virtual void WhoAreYou() {Console.WriteLine("I am an A"); }
}
class B : A {
public override void WhoAreYou() { Console.WriteLine("I am a B"); }
}
Thông điệp viện dẫn phương thức phụ thuộc vào kiểu dynamic của đối tượng.
A a = new B();
a.WhoAreYou(); // "I am a B"
Ưu điểm: mọi phương thức có thể làm việc với A đều làm việc được với B
void Use (A x) {
x.WhoAreYou();
}
Use(new A()); // "I am an A"
Use(new B()); // "I am a B"
ẨN
Trong subclass, các thành phần được khai báo với từ khoá new sẽ ẩn tất cả các thành phần có cùng tên và dấu hiệu trong baseclass.
class A {
public int x;
public void F() {...}
public virtual void G() {...}
}
class B : A {
public new int x;
public new void F() {...}
public new void G() {...}
}
B b = new B();
b.x = ...; // truy nhập tới B.x
b.F(); ... b.G(); // gọi tới B.F và B.G
((A)b).x = ...; // truy nhập tới A.x
((A)b).F(); ... ((A)b).G(); // gọi tới A.F và A.G
VÍ DỤ
class Animal {
public virtual void WhoAreYou() { Console.WriteLine("I am an animal"); }
}
class Cat : Animal {
public override void WhoAreYou() { Console.WriteLine("I am a cat"); }
}
Animal pet = new Cat();
pet.WhoAreYou(); // "I am a cat"
HÀM TẠO VÀ THỪA KẾ
PROTECTED VÀ INTERNAL
protected có thể được truy nhập trong cùng class và các subclass của nó
internal có thể được truy nhập trong cùng assembly
protected internal có thể được truy nhập trong cùng class và các subclass của nó, nhưng phải thuộc cùng một assembly
Ví dụ
class Stack {
protected int[] values = new int[32];
protected int top = -1;
public void Push(int x) {...}
public int Pop() {...}
}
class BetterStack : Stack {
public bool Contains(int x) {
foreach (int y in values) if (x == y) return true;
return false;
}
}
class Client {
Stack s = new Stack();
... s.values[0] ... // lỗi khi dịch
}
LỚP TRỪU TƯỢNG
Ví dụ
abstract class Stream {
public abstract void Write(char ch);
public void WriteString(string s) { foreach (char ch in s) Write(s); }
}
class File : Stream {
public override void Write(char ch) {... ghi ch vào bộ nhớ ...}
}
Chú ý
Các phương thức abstract không có phần cài đặt.
Nếu một lớp có phương thức abstract (do khai báo hoặc do kế thừa) thì lớp đó phải là lớp trừu tượng.
Không thể tạo ra đối tượng của lớp trừu tượng
THUỘC TÍNH VÀ INDEXER TRỪU TƯỢNG
Ví dụ
abstract class Sequence {
public abstract void Add(object x); // phương thức
public abstract string Name { get; } // thuộc tính
public abstract object this [int i] { get; set; } // indexer
}
class List : Sequence {
public override void Add(object x) {...}
public override string Name { get {...} }
public override object this [int i] { get {...} set {...} }
}
Chú ý
Các thuộc tính và indexer được chồng phải có cùng các phương thức get/set tương tự như trong baseclass
LỚP CÔ LẬP (SEALED CLASS)
Ví dụ
sealed class Account : Asset {
long val;
public void Deposit (long x) { ... }
public void Withdraw (long x) { ... }
...
}
Chú ý
Không thể kế thừa từ sealed class, nhưng sealed class vẫn kế thừa từ lớp khác.
Cũng có thể sử dụng từ khoá sealed với các phương thức
LỚP TỔNG QUAN (System.object)
System.Object là baseclass của tất cả các lớp
class Object {
protected object MemberwiseClone() {...}
public Type GetType() {...}
public virtual bool Equals (object o) {...}
public virtual string ToString() {...}
public virtual int GetHashCode() {...}
}
Sử dụng trực tiếp
Type t = x.GetType(); trả về kiểu được khai báo của x
object copy = x.MemberwiseClone(); tạo ra bản sao
Chồng trong subclass:
x.Equals(y) so sánh giá trị của x và y
x.ToString() trả về xâu biểu diễn x
int code = x.getHashCode(); trả về mã băm của x
VÍ DỤ VỀ LỚP System.object
class Fraction {
int x, y;
public Fraction(int x, int y) { this.x = x; this.y = y; }
...
public override string ToString() { return String.Format("{0}/{1}", x, y); }
public override bool Equals(object o) { Fraction f = (Fraction)o; return f.x == x && f.y == y; }
public override int GetHashCode() { return x ^ y; }
public Fraction ShallowCopy() { return (Fraction) MemberwiseClone(); }
}
class Client {
static void Main() {
Fraction a = new Fraction(1, 2);
Fraction b = new Fraction(1, 2);
Fraction c = new Fraction(3, 4);
Console.WriteLine(a.ToString()); // 1/2
Console.WriteLine(a); // 1/2 (ToString được gọi tự động)
Console.WriteLine(a.Equals(b)); // true
Console.WriteLine(a == c); // false
Console.WriteLine(a.GetHashCode()); // 3
a = c.ShallowCopy();
Console.WriteLine(a); // 3/4
}
}
INTERFACE - GIAO DIỆN
Đặng Thành Trung
CÚ PHÁP
public interface IList : ICollection, IEnumerable {
int Add (object value); // methods
bool Contains (object value);
...
bool IsReadOnly { get; } // property
...
object this [int index] { get; set; } // indexer
}
Interface là abstract class; không có phần cài đặt.
Có thể chứa methods, properties, indexers and events
(không chứa trường dữ liệu, hằng, hàm tạo, hàm huỷ, toán tử, kiểu lồng).
Các thành phần của interface là public abstract (virtual).
Các thành phần của interface không thể là static.
Class và struct có thể cài đặt nhiều interface.
Interface có thể mở rộng các interface khác.
interface được cài đặt bởi class và struct
class MyClass : MyBaseClass, IList, ISerializable {
public int Add (object value) {...}
public bool Contains (object value) {...}
...
public bool IsReadOnly { get {...} }
...
public object this [int index] { get {...} set {...} }
}
Một class chỉ kế thừa từ một baseclass, nhưng có thể cài đặt cho nhiều interface.
Một struct không kế thừa, nhưng có thể cài đặt cho nhiều interface.
Tất cả các thành phần của interface (method, property, indexer) đều phải được cài đặt hoặc được thừa kế.
Khi cài đặt các phương thức của interface không cần sử dụng từ khoá override, có thể sử dụng từ khoá virtual hoặc abstract .
Nếu phương thức Add của MyClass được chồng trong các subclass thì nó nên được khai báo với từ khoá virtual .
THỰC THI GIAO DIỆN
Gán: MyClass c = new MyClass();
IList list = c;
Gọi phương thức: list.Add("Tom"); // gán động => myClass.Add
Kiểm tra kiểu: if (list is MyClass) ... // true
if (list is ISerializable) ... // true
Ép kiểu: c = list as MyClass;
c = (MyClass) list;
ISerializable ser = (ISerializable) list;
VÍ DỤ
interface ISimpleReader {
int Read();
}
interface IReader : ISimpleReader {
void Open(string name);
void Close();
}
class Terminal : ISimpleReader {
public int Read() { ... }
}
class File : IReader {
public int Read() { ... }
public void Open(string name) { ... }
public void Close() { ... }
}
// null có thể được gán cho các biến của các kiểu interface
ISimpleReader sr = null; sr = new Terminal();
sr = new File();
IReader r = new File();
sr = r;
XUNG ĐỘT TÊN
Xảy ra khi có hai interface cơ sở có phương thức cùng tên
interface I1 {
void F();
}
interface I2 {
void F();
}
class B : I1, I2 {
//-----cài đặt phương thức F của class B
public void F() { Console.WriteLine("B.F"); }
//----- cài đặt phương thức F cho từng interface
void I1.F() { Console.WriteLine("I1.F"); }
void I2.F() { Console.WriteLine("I2.F"); } // phải là public
}
B b = new B();
b.F(); // B.F
I1 i1 = b;
i1.F(); // I1.F
I2 i2 = b;
i2.F(); // I2.F
DELEGATE - ỦY QUYỀN
Đặng Thành Trung
delegate
delegate là kiểu dữ liệu đặc biệt, có khả năng lưu trữ một tham chiếu tới phương thức có dấu hiệu cụ thể (tức là các tham số và kiểu trả về của phương thức).
delegate có thể viện dẫn các phương thức một cách động khi sự kiện xảy ra.
delegate không quan tâm tới những lớp đối tượng mà nó tham chiếu tới.
delegate LÀ KIỂU PHƯƠNG THỨC
Khai báo kiểu delegate
delegate void Notifier (string sender);// tương tự khai báo phương thức
// sử dụng từ khoá delegate
Khai báo biến delegate
Notifier greetings;
Gán một phương thức cho biến delegate
void SayHello(string sender) {
Console.WriteLine("Hello from " + sender);
}
greetings = new Notifier(SayHello);
Lời gọi biến delegate
greetings("John"); // viện dẫn phương thức SayHello("John") => "Hello from John"
GÁN PHƯƠNG THỨC KHÁC NHAU
Tất cả các phương thức phù hợp với delegate đều có thể được gán với biến delegate đó
void SayGoodBye(string sender) {
Console.WriteLine("Good bye from " + sender);
}
greetings = new Notifier(SayGoodBye);
greetings("John");// SayGoodBye("John") => "Good bye from John"
Chú ý
Biến delegate có thể được gán giá trị null (không có phương thức nào được gán cho nó).
Nếu biến delegate bằng null thì sẽ không được gọi
TẠO GIÁ TRỊ CỦA delegate
new DelegateType (obj.Method)
Biến delegate chứa phương thức và đối tượng nhận, nhưng không chứa tham số
new Notifier(myObj.SayHello);
Đối tượng có thể là this (và có thể bỏ qua)
new Notifier(SayHello);
Phương thức có thể là static. Trong trường hợp này, tên của class phải được thay thế cho đối tượng.
new Notifier(MyClass.StaticSayHello);
Phương thức không thể là abstract, nhưng có thể là virtual, override, hoặc new.
Dấu hiệu của phương thức phải trùng với dấu hiệu của DelegateType
- số lượng tham số
- kiểu dữ liệu của tham số (bao gồm cả kiểu trả về)
- kiểu truyền tham số (ref, out, value)
MULTICAST delegate
Biến delegate có thể chứa nhiều giá trị cùng một thời điểm
Notifier greetings;
greetings = new Notifier(SayHello);
greetings += new Notifier(SayGoodBye);
greetings("John"); // "Hello from John"
// "Good bye from John"
greetings -= new Notifier(SayHello);
greetings("John"); // "Good bye from John"
Chú ý
Nếu multicast delegate là một hàm, thì sẽ trả về giá trị của lời gọi cuối cùng
Nếu multicast delegate có tham số out, tham số của lời gọi cuối cùng sẽ được trả về. Tham số ref được truyền qua tất cả các phương thức.
Event là delegate đặc biệt
class Model {
public event Notifier notifyViews;
public void Change() { ... notifyViews("Model"); }
}
class View {
public View(Model m) { m.notifyViews += new Notifier(Update); }
void Update(string sender) { Console.WriteLine(sender + " was changed"); }
}
class Test {
static void Main() {
Model model = new Model();
new View(model); new View(model); ...
model.Change();
}
}
Tại sao phải sử dụng event để thay thế cho các biến delegate khác?
Chỉ class khai báo event thì mới được kích hoạt nó
Các class khác có thể thay đổi event bằng toán tử += hoặc -= (nhưng không sử dụng toán tử =)
BẮT SỰ KIỆN
Các delegate dùng để bắt sự kiện có dấu hiệu như sau:
delegate void SomeEvent (object source, MyEventArgs e);
Kiểu trả về: void
Tham số thứ nhất: Đối tượng gửi sự kiện (kiểu object)
Tham số thứ hai: sự kiện (subclass của System.EventArgs)
public class EventArgs {
public static readonly EventArgs Empty;
}
VÍ DỤ
public delegate void KeyEventHandler (object sender, KeyEventArgs e);
public class KeyEventArgs : EventArgs {
public virtual bool Alt { get {...} } // true nếu nhấn phím Alt
public virtual bool Shift { get {...} } // true nếu nhấn phím Shift
public bool Control { get {...} }// true nếu nhấn phím Ctrl
public bool Handled { get{...} set {...} } // cho biết có bắt được sự kiện không
public int KeyValue { get {...} }// mã phím
...
}
class MyKeyEventSource {
public event KeyEventHandler KeyDown;
public KeyPressed() {
KeyDown(this, new KeyEventArgs(...));
}
}
class MyKeyListener {
public MyKeyListener(...) { keySource.KeyDown += new KeyEventHandler(HandleKey);}
void HandleKey (object sender, KeyEventArgs e) {...}
}
EXCEPTION - NGOẠI LỆ
Đặng Thành Trung
LỆNH try
FileStream s = null;
try {
s = new FileStream(curName, FileMode.Open);
...
} catch (FileNotFoundException e) {
Console.WriteLine("file {0} not found", e.FileName);
} catch (IOException) {
Console.WriteLine("some IO exception occurred");
} catch {
Console.WriteLine("some unknown error occurred");
} finally {
if (s != null) s.Close();
}
Mệnh đề catch được kiểm tra theo thứ tự.
Mệnh đề finally luôn luôn được thực hiện (nếu xuất hiện)
Tên của Exception trong mệnh đề catch có thể được bỏ qua
Kiểu của Exception phải thừa kế từ System.Exception.
System.Exception
Thuộc tính
e.Message thông báo lỗi
e.StackTrace vết lưu stack lời gọi phương thức
e.Source ứng dụng hoặc đối tượng trả về Exception
...
Phương thức
e.ToString() Trả về tên của Exception và StackTrace
...
TRẢ VỀ Exception
Do các thao tác không hợp lệ (ngoại lệ không tường minh)
Chia cho 0
Tràn chỉ số
Truy nhập vào tham số có giá trị null
...
Do lệnh throw (ngoại lệ tường minh)
throw new FunnyException(10);
class FunnyException : ApplicationException {
public int errorCode;
public FunnyException(int x) { errorCode = x; }
}
(Do lời gọi tới một phương thức có trả ra ngoại lệ nhưng không được bắt)
s = new FileStream(...);
CẤU TRÚC EXCEPTION
Exception
SystemException
ArithmeticException
DivideByZeroException
OverflowException
...
NullReferenceException
IndexOutOfRangeException
InvalidCastException
...
ApplicationException
... user-defined exceptions
...
IOException
FileNotFoundException
DirectoryNotFoundException
...
WebException
...
TÌM KIẾM MỆNH ĐỀ catch
Chuỗi lời gọi sẽ được thực hiện cho đến khi có một phương thức có mệnh đề catch tương ứng được tìm thấy
Nếu không tìm thấy, chương trình sẽ báo lỗi
EXCEPTION TRONG delegate
Trong quá trình tìm kiếm mệnh đề catch, delegate cũng được coi như các hàm
exception trong multicast delegate
Nếu Exc1 được trả ra từ G() thì F() sẽ bắt và sau đó gọi H()
Nếu Exc2 được trả ra từ G() thì hàm Main() sẽ bắt và H() không được gọi nữa.
THƯ VIỆN
Đặng Thành Trung
TỔNG QUAN
Math
public sealed class Math {
public const double PI = 3.14....;
public const double E = 2.71...;
public static T Abs(T val); // T là kiểu: sbyte, short, int, long, float, double, decimal
public static T Sign(T val);
public static T1 Min(T1 x, T1 y); // T1, T là kiểu: byte, ushort, uint, ulong
public static T1 Max(T1 x, T1 y);
public static double Round(double x);
public static double Floor(double x);
public static double Ceiling(double x);
public static double Sqrt(double x);
public static double Pow(double x, double y); // xy
public static double Exp(double x) // ex
public static double Log(double x); // logex
public static double Log10(double x); // log10x
public static double Sin(double x); // góc tính theo radian
public static double Cos(double x);
public static double Tan(double x);
public static double Asin(double x);
public static double Acos(double x);
public static double Atan(double x);
}
Random
public class Random {
public Random();
public Random(int seed);
public virtual int Next(); // 0 <= res < int.MaxVal
public virtual int Next(int x); // 0 <= res < x
public virtual int Next(int x, int y); // x <= res < y
public virtual double NextDouble(); // 0.0 <= res < 1.0
public virtual void NextBytes(byte[] b); // các phần tử của mảng b là số ngẫu nhiên
}
String
public sealed class String : ICloneable, IComparable, IEnumerable {
public String(char[] a);
public String(char[] a, int start, int len);
public static string Copy(string s);
public static string Format(String s, params object[]);
public static string Join(string separator, string[] parts);
public static bool operator ==(bool a, bool b);
public static bool operator !=(bool a, bool b);
public int Length { get; }
public char this[int i] { get; }
public override bool Equals(object o);
public int CompareTo(object o);
public static int CompareOrdinal(string a, string b);
public void CopyTo(int from, char[] dest, int to, int len);
public bool StartsWith(string s);
public bool EndsWith(string s);
public int IndexOf(T s); // T ... string, char
public int LastIndexOf(T s);
public string Substring(int pos);
public string Substring(int pos, int len);
public string[] Split(params char[]);
public char[] ToCharArray();
...
}
Chuyển đổi giữa string và số
public sealed class Convert { // namespace System
public static string ToString(T x); // T là kiểu số bất kỳ
public static bool ToBoolean(string s);
public static byte ToByte(string s);
public static sbyte ToSByte(string s);
public static char ToChar(string s);
public static short ToInt16(string s);
public static int ToInt32(string s);
public static long ToInt64(string s);
public static ushort ToUInt16(string s);
public static uint ToUInt32(string s);
public static ulong ToUInt64(string s);
public static float ToSingle(string s);
public static double ToDouble(string s);
public static decimal ToDecimal(string s);
...
}
string s = Convert.ToString(123); // "123"
s = 123.ToString(); // "123"
int i = Convert.ToInt32(s); // 123
StringBuilder
public sealed class StringBuilder { // namespace System.Text
public StringBuilder();
public StringBuilder(int initCapacity);
public StringBuilder(string s);
public StringBuilder(string s, int from, int len);
public int Length { get; set; }
public int Capacity { get; set; }
public char this[int i] { get; set; }
public StringBuilder Append(T x); // T là kiểu string hoặc kiểu số
public StringBuilder Insert(int pos, T x);
public StringBuilder Remove(int pos, int len);
public StringBuilder Replace(T x, T y); // T là kiểu string, char
public bool Equals(object x);
public string ToString();
...
}
Ví dụ
StringBuilder b = new StringBuilder("A.C");
b.Insert(2, "B.");
b.Replace('.', '/');
Console.WriteLine(b.ToString()); // A/B/C
Collection class
Array
Tất cả các mảng đều kế thừa từ lớp Array
public abstract class Array : IList, ICloneable, IEnumerable{ // namespace System
public int Length { get; } // tổng số các phần tử trong các chiều
public int Rank { get; } // tổng số các chiều
public static void Clear(Array a, int pos, int length);
public static void Copy(Array src, int from, Array dst, int to, int len);
public static int IndexOf(Array a, object x);
public static int LastIndexOf(Array a, object x);
public static void Reverse(Array a);
public static void Sort(Array a);
public static int BinarySearch(Array a, object x);
public virtual object Clone();
public virtual bool Equals(object x);
public int GetLength(int dimension);
...
}
Ví dụ
int[] a = {17, 3, 5, 9, 6};
Array.Sort(a);
Array.Reverse(a);
foreach (int x in a) Console.Write("{0} ", x); // 17 9 6 5 3
ArrayList
Mảng động
public class ArrayList : IList, IEnumerable, ICloneable { // namespace System.Collections
public ArrayList();
public ArrayList(int initCapacity);
public ArrayList(ICollection c);
public virtual int Count { get; }
public virtual int Capacity { get; set; }
public virtual object this[int i] { get; set; }
public virtual void Clear();
public virtual int Add(object x);
public virtual void Insert(int pos, object x);
public virtual void Remove(object x);
public virtual void RemoveAt(int pos);
public virtual object Clone();
public virtual bool Equals(object x);
public virtual bool Contains(object x);
public virtual int IndexOf(object x);
public virtual void Reverse();
public virtual void Sort();
public virtual int BinarySearch(object x);
...
}
Ví dụ ArrayList
using System;
using System.Collections;
class Test {
static void Main(string[] arg) {
ArrayList a = new ArrayList();
foreach (string s in arg) a.Add(s);
a.Sort();
Console.Write("a = ");
for (int i = 0; i < a.Count; i++) Console.Write("{0} ", a[i]);
Console.WriteLine();
int pos = a.BinarySearch("Tom");
if (pos >= 0)
Console.WriteLine("Tom found at {0}", pos);
else
Console.WriteLine("Tom not found");
}
}
Aufruf: Test John Tom Alice Charly
Ausgabe:
a = Alice Charly John Tom
Tom found at 3
ListDictionary
Tương tự như danh sách liên lết
public class ListDictionary : IDictionary, IEnumerable { // System.Collections.Specialized
public ListDictionary();
public int Count { get; }
public ICollection Keys { get; }
public ICollection Values { get; }
public object this[object key] { get; set; }
public void Clear();
public void Add(object key, object value);
public void Remove(object key);
public bool Contains(object key);
public virtual bool Equals(object x);
public IDictionaryEnumerator GetEnumerator();
...
}
Ví dụ
ListDictionary population = new ListDictionary();
population["Vienna"] = 1592600;
population["London"] = 7172000 ;
population["Paris"] = 2305000;
...
foreach (DictionaryEntry x in tab) Console.WriteLine("{0} = {1}", x.Key, x.Value);
Hashtable
public class Hashtable : IDictionary, IEnumerable, ISerializable, ICloneable { // System.Collections
public HashTable();
public virtual object Clone();
public virtual bool ContainsKey(object key);
public virtual bool ContainsValue(object value);
}
Ví dụ
Hashable population = new Hashtable();
population["Vienna"] = 1592600;
population["London"] = 7172000;
population["Paris"] = 2305000;
...
foreach (DictionaryEntry x in population) Console.WriteLine("{0} = {1}", x.Key, x.Value);
SortedList
Là mảng và được sắp xếp bởi Key
public class SortedList : IDictionary, IEnumerable, ICloneable { // System.Collections
public SortedList(); // variants exist
//... gleiche Members wie Hashtable; zusätzlich:
public virtual int Capacity { get; set; }
public virtual object GetKey(int i); // returns Key[i]
public virtual object GetByIndex(int i); // returns Value[i]
public virtual void SetByIndex(int i, object value);
public virtual IList GetKeyList();
public virtual IList GetValueList();
public virtual int IndexOfKey(object key);
public virtual int IndexOfValue(object value); // returns index of the first occurrence of value
public virtual void RemoveAt(int i);
}
Ví dụ
SortedList population = new SortedList();
population["Vienna"] = 1592600;
population["London"] = 7172000;
...
foreach (DictionaryEntry x in population) Console.WriteLine("{0} = {1}", x.Key, x.Value);
// in ra các phần tử được sắp xếp theo Key
Stack
public class Stack : ICollection, IEnumerable, ICloneable { // System.Collections
public Stack();
public Stack(int initCapacity);
public virtual int Count { get; }
public virtual void Clear();
public virtual void Push(object x);
public virtual object Pop();
public virtual object Peek();
public virtual bool Contains(object x);
public virtual object Clone();
public virtual IEnumerator GetEnumerator();
public virtual object[] ToArray();
...
}
Ví dụ
Stack s = new Stack();
for (int i = 0; i <= 20; i++) s.Push(i);
while (s.Count > 0) Console.Write("{0} ", (int)s.Pop()); // trả về 20 19 18 17 ... 0
Queue
public class Queue : ICollection, IEnumerable, ICloneable { // System.Collections
public Queue();
public virtual int Count { get; }
public virtual void Clear();
public virtual void Enqueue(object x);
public virtual object Dequeue();
public virtual object Peek();
public virtual bool Contains(object x);
public virtual object Clone();
public virtual IEnumerator GetEnumerator();
public virtual object[] ToArray();
...
}
Ví dụ
Queue q = new Queue();
q.Enqueue("one");
q.Enqueue("two");
q.Enqueue("three");
foreach (string s in q) Console.Write(s + " "); // one two three
// q vẫn đầy
Console.WriteLine();
while (q.Count > 0) Console.Write((string)q.Dequeue() + " "); // one two three
// q rỗng
Stream class
Ví dụ FileStream
using System;
using System.IO;
class Test {
static void Copy(string from, string to, int pos) {
try {
FileStream sin = new FileStream(from, FileMode.Open);
FileStream sout = new FileStream(to, FileMode.Create);
sin.Seek(pos, SeekOrigin.Begin); // di chuyển tới cuối file
int ch = sin.ReadByte();
while (ch >= 0) {
sout.WriteByte((byte)ch);
ch = sin.ReadByte();
}
sin.Close();
sout.Close();
} catch (FileNotFoundException e) {
Console.WriteLine("-- file {0} not found", e.FileName);
}
}
static void Main(string[] arg) {
Copy(arg[0], arg[1], 10);
}
}
Bạn đang đọc truyện trên: AzTruyen.Top