Các thành phần của lớp

Giới thiệu các thành phần cơ bản của lớp trong C#

Một lớp được coi như là một khuôn mẫu hay thiết kế mô hình cho các đối tượng ở trong C#. Vậy có nghĩa khi cần tạo ra các đối tượng trong ứng dụng C#, bạn phải định nghĩa lớp trước. Sau đây là các thành phần cơ bản của một lớp trong C#:

  • Biến trường dữ liệu (instance variable): các biến này là khai báo các trường dữ liệu trong lớp và nằm bên ngoài các phương thức. Các biến này được khởi tạo khi bạn khởi tao một đối tượng thể hiện của lớp đó. Bạn có thể truy xuất vào các biến này trong phương thức, hàm khởi tạo hoặc từ một lớp khác.
  • Biến lớp (class variable): các biến này được khai báo giống như biến trường dữ liệu. Tuy nhiên khác ở chỗ trước biến này sẽ có bổ từ static và bên ngoài có thể truy xuất trực tiếp tới giá trị của biến thông qua tên lớp.
  • Phương thức khởi tạo – Hàm dựng (constructor): đây là phương thức đặc biệt nhằm khởi tạo một đối tượng cụ thể của một lớp. Phương thức khởi tạo có cùng tên với tên lớp nhưng không có giá trị trả về một cách tường minh.
  • Phương thức hoạt động (method): phương thức hoạt động là một tập hợp các câu lệnh được nhóm lại với nhau nhằm thực hiện một thao tác cụ thể

Biến sử dụng trong lớp

Trong lớp, chúng ta thường gặp các loại biến dữ liệu sau:

  • Biến chứa các thông tin của lớp – hay còn gọi là biến trường dữ liệu (field)
  • Biến trường dữ liệu của các đối tượng thể hiện – hay còn gọi là biến cục bộ (local variable)
  • Biến trong khai báo phương thức – hay còn gọi là tham số (parameter)

Khai báo biến trường dữ liệu

Khai báo biến trường dữ liệu giống hệt như khai báo các biến thông thường. Chúng ta quay lại ví dụ ở học phần trước, lớp Car có các dòng khai báo biến trường dữ liệu như sau:

public string make;
public string model;
public string color;

Về cú pháp khai báo các trường dữ liệu sẽ gồm các phần sau:

  1. Có hoặc có 1 hoặc nhiều bổ từ như public hay private
  2. Kiểu của trường dữ liệu đó
  3. Tên trường dữ liệu

Vậy có nghĩa là lớp Car có các trường sau make, model color với cùng kiểu dữ liệu là string. Từ khoá public đặt ở đầu được hiểu có nghĩa là các trường này công cộng và bạn có thể truy xuất thoải mái tới giá trị của các trường này từ lớp bên ngoài.

Bổ từ truy xuất

Bổ từ ngoài cùng bên trái được sử dụng để cho phép bạn kiểm soát xem các lớp khác có quyền truy xuất các trường dữ liệu của lớp hiện tại. Hiện tại, bạn hãy làm quen với 2 bổ từ publicprivate còn các bổ từ khác ta sẽ dần dần tìm hiểu sau:

  • public: bổ từ này đứng trước trường dữ liệu có nghĩa là cho phép tất cả các lớp đều có thể truy xuất tới trường dữ liệu này
  • private: bổ từ này đứng trước trường dữ liệu có nghĩa là sẽ không cho phép các lớp khác truy xuất tới trường dữ liệu này

Theo đặc tính đóng gói của ngôn ngữ C#, thông thường các lập trình viên sẽ ẩn các thông tin của lớp đi bằng cách đặt các bổ từ private trước khai báo các trường dữ liệu. Điều này có nghĩa là bạn chỉ có thể truy xuất trực tiếp các trường dữ liệu ở bên trong lớp. Còn ở bên ngoài, bạn sẽ không thể truy xuất tới các trường dữ liệu vì giờ chúng đang có tính chất riêng tư. Tuy nhiên, trong C# bạn có thể cung cấp cách thức để các lớp bên ngoài truy xuất tới trường dữ liệu trong lớp hiện tại thông qua việc tạo các property. Tuỳ thuộc vào mục đích và yêu cầu trong thiết kế mã cho các lớp chúng ta sẽ có 3 mức truy xuất tới từng trường dữ liệu như sau:

  • Property có thể đọc và ghi
  • Property chỉ đọc
  • Property chỉ ghi

Tiếp tục sử dụng lớp Car ở trên, chúng ta sẽ sửa đổi như sau:

public class Car {
    private string make;
    private string model;
    private string color;
    public String getMake() {
        return make;
    }
    public void SetMake(string _make) {
        make = _make;
    }
    public string GetModel() {
        return model;
    }
    public void SetModel(string _model) {
        model = _model;
    }
    public string getColor() {
        return color;
    }
    public void SetColor(string _color) {
        color = _color;
    }
}

Kiểu của trường dữ liệu

Tất cả các trường dữ liệu đều phải có kiểu cụ thể. Bạn có thể sử dụng các kiểu dữ liệu nguyên thuỷ như int, float, boolean,… hay là các kiểu tham chiếu như chuỗi ký tự (String), mảng (Array), hay đối tượng (Object).

Quy ước đặt tên biến, lớp và phương thức

Tất cả các biến bao gồm cả trường dữ liệu, biến cục bộ hay tham số đều phải tuân theo quy tắc đặt tên biến của C#. Bạn có thể tham khảo thêm phần “Đặt tên biến” ở bài học “Lập trình cơ bản trong C#”.

Ở đây chúng tôi, xin lưu ý một số yêu cầu khi đặt tên liên quan đến lớp, trường dữ liệu và phương thức:

  • Ký tự đầu tiên của tên lớp phải là chữ cái viết hoa và là danh từ
  • Ký tự đầu tiên của các trường dữ liệu phải là chữ cái viết thường
  • Ký tự đầu tiên của phương thức phải là chữ cái viết hoa và từ đầu tiên trong tên phương thức phải là động từ

Phương thức hoạt động

Một phương thức hoạt động là một tập hợp các câu lệnh được nhóm lại với nhau để thực hiện một thao tác cụ thể. Ví dụ, khi bạn gọi phương thức Console.WriteLine(), hệ thống thực sự phải chạy một vài câu lệnh trong phương thức WriteLine() để hiển thị một thông báo lên mành hình console.

Bây giờ chúng ta sẽ tìm hiểu cách thức để xây dựng các phương thức rồi gọi chúng trong ứng dụng.

Tạo phương thức

Định nghĩa phương thức bao gồm có 2 phần: phần đầu và phần thân của phương thức. Cú pháp định nghĩa phương thức như sau:

modifier returnType NameOfMethod (Parameter List) {
   // method body
}

Cú pháp trên bao gồm các thành phần sau:

  • Bổ từ (modifier): định nghĩa kiểu truy xuất tới phương thức này (public hay private); hay kiểu truy xuất trực tiếp thông qua lớp (static) và khai báo bổ từ là tuỳ chọn.
  • Kiểu trả về (returnType): bất cứ phương thức khi chạy đều phải có giá trị trả về. Nếu không trả về gía trị gì thì dùng kiểu void.
  • Tên phương thức (NameOfMethod): tên của phương thức, từ bắt đầu nên là động từ và mô tả rõ ý nghĩa của phương thức.
  • Danh sách tham số (Parameter List): danh sách tham số bao gồm khai báo các biến tham số và ngăn cách nhau bằng dấu ,. Nếu phương thức không cần tham số thì bạn có thể để trống phần này.
  • Thân phương thức (method body): thân phương thức định nghĩa nội dung bao gồm các câu lệnh mà phương thức sẽ thực thi.

Ví dụ sau sẽ mô tả phương thức Sum() có 2 tham số là 2 giá trị số nguyên và trả về một giá trị tổng số nguyên của 2 tham số đầu vào:

public class MathClass {
   public int Sum(int a, int b) {
      int result;
      result = a + b;
      return result;
   }
}

Gọi phương thức

Sau khi khai báo phương thức, tiếp theo bạn sẽ cần phải gọi phương thức đó. Có 2 trường hợp khi gọi phương thức:

  1. Gọi phương thức mà không có giá trị trả về
  2. Gọi phương thức và có giá trị trả về

Quá trình gọi phương thức hết sức đơn giản. Khi một chương trình gọi phương thức, quyền điều khiển trong chương trình sẽ chuyển tới phương thức được gọi. Phương thức được gọi sẽ thực thi cho tới khi:

  • gặp câu lệnh return
  • gặp dấu đóng ngoặc xoắn }, báo hiệu kết thúc phương thức

Ví dụ về gọi phương thức mà không có giá trị trả về là tình huống đơn giản khi bạn gọi phương thức WriteLine() của lớp Console như sau:

Console.WriteLine("This is VTC Academy!");

Bạn sẽ thấy phương thức trên chỉ in ra màn hình một thông điệp và không có giá trị trả về sau khi gọi phương thức.

Tiếp tục ta sẽ xây dựng lớp Program để gọi phương thức Sum() như sau:

public class Program {
   public static void Main(string[] args) {
      int a = 6;
      int b = 9;
      int result;
      MathClass math = new MathClass();
      result = math.Sum(a, b);
      Console.WriteLine("Sum = " + result);
   }
}

Do phương thức Sum() khi gọi sẽ trả về giá trị nên bạn phải thực hiện hành động gọi phương thức Sum() cho một biến có kiểu tương ứng với kiểu trả về của phương thức:

result = math.Sum(a, b);

Chương trình MethodDemo sau đây sẽ minh hoạ đầy đủ cách khai báo và gọi phương thức như đã trình bầy ở trên:

Phương thức static

Nếu bạn muốn gọi trực tiếp phương thức sum() thông qua tên lớp MathClass mà không cần phải khởi tạo thể hiện từ MathClass thì bạn thêm bổ từ static sau trong dòng khai báo phương thức Sum() như sau:

public static int Sum(int a, int b) {
   ...
}

Khi đó phương thức Sum() còn gọi là phương thức static và cú pháp gọi phương thức Sum() sẽ như sau:

int result = MathClass.Sum(6, 9);

Từ khoá void

Từ khoá void cho phép chúng ta tạo ra các phương thức mà khi chạy sẽ không trả về giá trị. Sau đây là ví dụ về sử dụng void:

Nạp chồng phương thức (Method Overloading)

Khi 1 lớp có 2 hay nhiều phương thức giống tên nhau nhưng khác tham số thì tình huống này được gọi là nạp chồng phương thức. Bạn chú ý, tình huống này khác so với ghi đè phương thức (method overriding) ở chỗ ghi đè phương thức thì các phương thức phải giống cả tên lẫn danh sách tham số đầu vào.

Tiếp tục ví dụ về MathClass ở trên, ta sẽ thêm 1 phương thức Sum() với kiểu dữ liệu double như sau:

public class MathClass {
   public int Sum(int a, int b) {
      int result;
      result = a + b;
      return result; 
   }
   public double Sum(double a, double b) {
      double result;
      result = a + b;
      return result; 
   }
 }

Trong lớp Program ta sẽ sẽ gọi cả 2 phương thức Sum() như sau:

public class Program {
   public static void Main(string[] args) {
      int a = 6;
      int b = 9;
      int intResult;
      double x = 6.9;
      double y = 9.6;
      double doubleResult;
      MathClass math = new MathClass();
      intResult = math.Sum(a, b);
      Console.WriteLine("Integer Sum = " + intResult);
      doubleResult = math.Sum(x, y);
      Console.WriteLine("Double Sum = " + doubleResult);
   }
}

Kỹ thuật nạp chồng phương thức giúp cho chương trình dễ đọc và dễ nhớ mã lệnh hơn. Bạn không phải nhớ nhiều phương thức với tên khác nhau mà chỉ cần nhờ 1 cái tên và khi cần xử lý khác nhau một chút thì ta sẽ thay đổi tham số đầu vào.

Chương trình MethodOverloadingDemo sau đây sẽ minh hoạ đầy đủ cách sử dụng nạp chồng phương thức như đã trình bầy ở trên:

Phương thức khởi tạo – Hàm dựng (Constructor)

Phương thức khởi tạo hay hàm dựng là một phương thức đặc biệt nhằm khởi tạo đối tượng thể hiện từ lớp đó. Phương thức khởi tạo có cú pháp giống hệt một phương thức bình thường và có tên giống với tên của lớp (bao gồm cả hoa thường phân biệt). Tuy nhiên, phương thức khởi tạo sẽ không có kiểu trả về một cách tường minh.

Thông thường, bạn sẽ sử dụng phương thức khởi tạo để đưa ra các giá trị khởi tạo ban đầu cho đối tượng thể hiện được tạo ra bởi lớp đó hoặc thực hiện bất cứ thủ tục khởi động cần thiết khác để tạo ra đối tượng hình thành đầy đủ.

Tất cả các lớp trong C# đều có phương thức khởi tạo cho dù bạn có tạo hay không tạo nó. Nếu bạn không tạo phương thức khởi tạo thì C# sẽ tự động cung cấp 1 phương thức khởi tạo mặc định và khởi tạo các trường dữ liệu với giá trị 0 hoặc null.

Ví dụ về phương thức khởi tạo:

public class MyClass {
   public int x;
   // Constructor
   public MyClass() {
      x = 10;
   }
}

Tiếp tục bạn tạo một lớp mới là Program như sau:

public class Program {
   public static void Main(string[] args) {
      MyClass c1 = new MyClass();
      MyClass c2 = new MyClass();
      Console.WriteLine("c1.x = " + c1.x);
      Console.WriteLine("c2.x = " + c2.x);
   }
}

Bạn sẽ thấy là cho dù bạn có tạo hàng trăm thể hiện từ lớp MyClass thì chúng sẽ đều giống nhau hết (cùng có giá trị trường x = 10). Vậy nếu như bạn muốn rằng các thể hiện từ lớp MyClass có thể khác nhau về giá trị trường dữ liệu thì bạn sẽ phải tạo các phương thức khởi tạo có tham số.

Phương thức khởi tạo có tham số

Ngoài các phương thức khởi tạo không tham số và tạo ra các đối tượng thể hiện của lớp với các trường dữ liệu có giá trị giống nhau thì bạn có thể tạo thêm các phương thức khởi tạo với các tham số khác nhau để khi tạo các đối tượng thể hiện chúng ta sẽ có các thể hiện khác nhau.

Tiếp tục ví dụ trên, chúng ta sẽ thêm một phương thức khởi tạo có 1 tham số như sau:

class MyClass {
   public int x;
   // Constructor without parameter
   public MyClass() {
      x = 10;
   }
   // Constructor with parameters
   public MyClass(int y) {
      x = y;
   }
}

Tiếp theo trong lớp Program ta sẽ cập nhật như sau:

public class Program {
   public static void Main(string[] args) {
      MyClass c1 = new MyClass(10);
      MyClass c2 = new MyClass(20);
      Console.WriteLine("c1.x = " + c1.x);
      Console.WriteLine("c2.x = " + c2.x);
   }
}

Bạn thấy đó, giờ 2 đối tượng thể hiện của lớp MyClass đã có giá trị trường dữ liệu x khác nhau.

Chương trình ConstructorDemo sau đây sẽ minh hoạ đầy đủ cách sử dụng các loại phương thức khởi tạo như đã trình bầy ở trên:

Từ khoá this

this là một từ khoá trong C# được sử dụng như là 1 tham chiếu đến đối tượng của lớp hiện tại. Sử dụng từ khoá thisbạn có thể tham chiếu đến các thành phần của lớp như phương thức khởi tạo, trường dữ liệu hay phương thức.

Chú ý: Từ khoá this chỉ được sử dụng và gọi bên trong phương thức hay phương thức khởi tạo

Ví dụ về sử dụng từ khoá this như sau:

public class Student {
   public int age;
   // Call another constructor in current constructor
   public Student() {
      this(20);
   }
   // Access current object's fields
   public Student(int age) {
      this.age = age;    
   }

}

Complete and Continue