- Phương thức (hàm) hủy
- Quá tải toán tử C#
- Thành viên tĩnh (static)
- Biến chỉ đọc trong lớp
- Lớp tĩnh static class
- Indexer - Bộ chỉ mục C#
Hàm (phương thức) hủy của lớp (Finalizer hoặc Destructor)
- Một lớp chỉ được khai báo một phương thức hủy (trong khi có thể có nhiều phương thức tạo)
- Bạn không thể gọi phương thức hủy một cách chủ động được (do hệ thống quản lý NET CORE tự quyết định thi hành nó khi nào)
- Khai báo phương thức hủy: tên trùng tên lớp, phía trước code ký hiệu
~
, phương thức không được có tham số.
class MyClass { ~MyClass() { // Thân phương thức hủy } }
Trong .NET có dịch vụ hệ thống tên GC (garbage collector), nó là dịch vụ được chạy một cách
tự động nhằm thu hồi bộ nhớ do .NET cấp phát trên HEAP không còn dùng đến quản lý bởi .NET.
GC
được thi hành tự động khi hệ thống thấy thiếu bộ nhớ
- mặc dù bạn có thể yêu cấu .NET tiến hành thu hồi bộ nhớ
không còn dùng đến ngày bằng cách gọi GC.Collect();
Bộ nhớ trên HEAP là nơi lưu các đối tượng được tạo ra từ lớp (toán tử new), khi đối tượng đó không còn biến nào tham chiếu (trỏ) đến thì nó sẽ được đánh dấu thu hồi - và khi GC thu hồi - phương thức hủy sẽ được thi hành.
Product
có cả phương thức tạo và hủy//DestructorExample.cs class Product { private string product_name; public Product(string name) { this.product_name = name; Console.WriteLine("Tạo - " + product_name); } ~Product() { Console.WriteLine("Hủy - " + product_name); } }
Sử dụng lớp Product tạo đối tượng như sau:
// Program.cs using System; namespace CS011_ClassAdvanced { class Program { static void TestConstructor() { Product p = new Product("ABC"); // Tạo đối tượng, biến p tham chiếu đến đối tượng p = null; // Biến p gán bằng null, đối tượng tạo ra phía trên, // không còn biến nào tham chiếu đến => Nó được đánh dấu // sẽ bị thu hồi bởi GC, lúc nào GC chạy do NET quyết định } static void Main(string[] args) { TestConstructor(); // Ép hệ thống thu hồi bộ nhớ (thực tế không cần ép, nó sẽ tự chạy khi cần) GC.Collect(); } } }
// Tạo - ABC // Hủy - ABC
Bạn thấy phương thức HỦY chạy khi đối tượng không còn tham chiếu nào đến nó, tại đây thích hợp để bạn thực hiện các thao tác cần giải phóng tài nguyên như Bô nhớ, Đóng file, ngắt kết nối ...
Quá tải toán tử trong C#
Quá tải toán tử (Operator Overloading) trong C#, giúp bạn định nghĩa mới (hoặc định nghĩa lại) hoạt động của các toán tử trên những đối tượng lớp do bạn định nghĩa.
Ví dụ, bạn có một lớp MyVector
như sau:
class MyVector { double x; double y; public MyVector(double x, double y) { this.x = x; this.y = y; } public void ShowXY() { Console.WriteLine("x = " + x); Console.WriteLine("y = " + y); } }
Giờ bạn muốn có phép toán +
trên những đối tượng sinh ra từ lớp này, ví dụ:
MyVector v1 = new MyVector(2,3); MyVector v2 = new MyVector(3,4); MyVector v3 = v1 + v2;
Nghĩa là v1 + v2
sẽ tạo ra một đối tượng mới
Để định nghĩa toán tử +
, nó định nghĩa giống phương thức thay tên bằng từ khóa
operator
và ký hiệu toán tử +
, toán tử có kiểu trả về MyVector
,
hai toán hạng bên trái và bên phải đểu là đối tượng lớp MyVector
nghĩa làm tham số
trong định nghĩa là 2 đối tượng này.
Cho thêm định nghĩa sau vào lớp MyVector
public static MyVector operator+(MyVector a, MyVector b) { double sx = a.x + b.x; double sy = a.x + b.y; MyVector v = new MyVector(sx,sy); return v; }
Giờ đã có thể sử dụng toán tử +
chạy thử đoạn code
MyVector v1 = new MyVector(2,3); MyVector v2 = new MyVector(3,4); MyVector v3 = v1 + v2; v3.ShowXY();
Kết quả
x = 5 y = 6
Một số toán tử có thể chỉ cần một số hạng, khi định nghĩa thì khai báo có một tham số.
Thành viên tĩnh trong lớp C#
Các thành viên trong lớp (phương thức, biến, thuộc tĩnh) khi khai báo có từ khóa static
ở đầu thì nó là thành viên tĩnh. Thành viên tĩnh thì nó không thuộc về đối tượng cụ thể nào, có thể sử
dùng mà không cần tạo đối tượng. Truy cập đến thành viễn tĩnh thông qua TÊN-LỚP.tên-thành-viên-tĩnh.
Đối với biến tĩnh thì dùng cho cho mọi đối tượng thuộc lớp, được khởi tạo một lần duy nhất. Nếu muốn khởi tạo thành viên tĩnh của lớp khi lần đầu truy cập có thể dùng phương thức khởi tạo tĩnh - xem thêm tại Static Constructor
Ví dụ biến tĩnh
class CountNumber { public static int num = 0; public void count() { num++; } public int getNum() { return num; } }
Sử dụng:
CountNumber c1 = new CountNumber(); CountNumber c2 = new CountNumber(); c1.count(); // CountNumber.num = 1; c2.count(); // CountNumber.num = 2; c1.count(); // CountNumber.num = 3; c2.getNum(); // trả ve 3
Ví dụ phương thức tĩnh
class MethodStatic { public static int Sum(int a, int b) { return a + b; } }
Sử dụng
MethodStatic.Sum(1,2); // tra về 3 - gọi phương thức mà không cần tạo đối tượng
Thành viên biến chỉ đọc (readonly) trong lớp C#
Để khai báo một biến trở thành dạng chỉ đọc thì thêm vào đó Modify có tên là readonly
. Ví dụ:
public readonly string name;
Biến readonly có nghĩa là chỉ đọc, không sửa đổi được nữa. Tuy nhiên so với hằng số const
thì có mấy điểm khác như sau:
- Hằng số thì phải khởi tạo ngay giá trị cho nó khi khởi tạo, biến readonly thì không khởi tạo ngay cũng được
- Biến readonly có thể gán giá trị cho nó trong hàm khởi tạo (và giá trị gán theo kết quả của một biểu thức nào đó)
class Student { // khai báo biến readonly public readonly string name; public Student(string name) { // khởi tạo biến readonly ở hàm tạo (bắt buộc nếu biến khai báo mà chưa khởi tạo) this.name = name; } }
Sử dụng:
Student s = new Student("Abc"); // khởi tạo biến readonly trong hàm tạo string n = s.name; // đọc biển readonly s.name = "AA"; // lỗi - vì không thể gán - chỉ có thể đọc
Lớp tĩnh static class trong C#
Khi khai báo lớp, bạn có thể khai báo nó là lớp tĩnh bằng cách thêm Modify static
trước tên lớp. Ví dụ:
public static class MyStaticClass { // ... các thành viên tĩnh }
Khi MyStaticClass
khai bão là lớp tĩnh như trên, thì các thành viên khai báo trong lớp
đều phải khai báo là thành viên tĩnh. Thường dùng lớp tĩnh đề gom các hàm tiện ích lại với nhau.
Ví dụ trong C# có lớp Math
là lớp tĩnh chứa các phương thức toán học dùng ngày như:
Math.Abs()
Math.Sin() ...
Bộ đánh chỉ mục indexer trong C#
Indexer
là khả năng cho cho phép truy cập đến các thành viên của lớp, thực hiện một số tác vụ
thông qua ký hiệu chỉ mục [chỉ-mục]
(ký hiệu chỉ mục được dùng để truy cập
các phần tử mảng - trình bày ở phần mảng C#)
Ví dụ, một đối tượng obj
nếu có hỗ trợ bộ chỉ mục, số nguyên thì có thể truy cập
đến dữ liệu, phương thức bằng cách viết obj[0], obj[1] ...
hay hỗ trợ chỉ mục dạng chuỗi thì
truy cập bằng obj["index-a"], obj["adress"] ...
Để khai báo một bộ chỉ mục, khai báo gần giống như cú pháp khai báo thuộc tính lớp, setter/getter , cú pháp cơ bản như sau:
public kiểu_trả_về this[kiểu_index index] { get { // thực hiện các tác vụ và trả về dữ liệu có kiểu_trả_về } set { // giá trị được truyền trong biến value, có thể lưu nó vào nơi thích hợp } }
Ví dụ có lớp IndexerExam
chứa hai thành viên dữ liệu ho
và ten
,
ta thử định nghĩa bộ chỉ mục số nguyên, nếu chỉ mục là 0 thì truy cập đến dữ liệu họ và 1 là ten.
class IndexerExam { string ho = "Nguyễn"; string ten = "Nam"; // Bộ chỉ mục số nguyên, chỉ mục là 0 hoặc 1 nếu khác sẽ phát sinh Exception public string this[int index] { // Đọc dữ liệu theo chỉ mục get { if (index == 0) return ho; else if (index == 1) return ten; else throw new Exception("Chỉ số không tồn tại"); } // Gán dữ liệu theo chỉ mục set { if (index == 0) ho = value; else if (index == 1) ten = value; else throw new Exception("Chỉ số không tồn tại"); } } }
Trong đó
throw new Exception("Chỉ số không tồn tại");
Là dòng phát sinh ra một Exception, khi cố gắng truy cập bằng một giá trị index không cho phép, kỹ thuật phát sinh Exception - lỗi sẽ tìm hiểu sau.
Giờ thì đối tượng lớp IndexerExam
truy cập được bằng ký hiệu []
, ví dụ:
IndexerExam obj = new IndexerExam(); Console.WriteLine(obj[0] + " " + obj[1]); // đọc obj.ho và obj.ten obj[0] = "Đinh"; // gán obj.ho obj[1] = "Quang Hưng"; // gán obj.name Console.WriteLine(obj[0] + " " + obj[1]); // đọc obj.ho và obj.ten // Kết quả chạy: // Nguyễn Nam // Đinh Quang Hưng
Tương tự bạn có thể tạo nhiều bộ chỉ mục indexer tương ứng với kiểu chỉ mục muốn đưa vào []
Mã nguồn CS011_ClassAdvanced (git), hoặc tải về tại ex011