C# cơ bản .NET Core

Hàm (phương thức) hủy của lớp (Finalizer hoặc Destructor)

Ở phần cơ bản về lớp, đã biết phương thức khởi tạo - phương thức này chạy khi tạo đối tượng lớp, ngược lại khi đối tượng bị hủy (giải phóng) nó sẽ tự động thi hành một phương thức gọi là phương thức hủy (hàm hủy Finalizer hay Destructor). Dùng phương thức hủy khi có nhu cầu dọn dẹp, giải phóng tài nguyên chiếm giữ ...
Khi sử dụng phương thức hủy lưu ý:
  • 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
    }
}
Khi nào hàm hủy được gọi - cơ chế dọn dẹp bộ nhớ với GC

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.

VÍ DỤ
Tạo một lớp là 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();

        }
    }
}
Chạy chương trình - có kết quả:
// 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 hoten, 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


Đăng ký nhận bài viết mới