Khai báo phương thức trong C#

Phương thức (method, gần giống function hàm trong lập trình hướng thủ tục) trong C# là một nhóm các lệnh nhằm thực hiện một tác vụ nào đó, dùng phương thức để sử dụng lại code, dễ dàng kiểm tra và bảo trì ứng dụng. Phương thức có thể thuộc về một đối tượng lớp nào đó, hoặc không tuy nhiên khai báo phương thức thì phải khai báo trong một lớp nào đó.

Lớp là một cách tổ chức code của lập trình hướng đối tượng - ta sẽ tìm kiểu kỹ về lớp ở các phần sau.

Xem lại chương trình C# đầu tiên ở phần 1, bạn thấy có một hàm Main được khai báo bên trong thân của lớp tên là Program, hàm này mặc dù khai báo trong lớp Program nhưng có từ khóa static ở phía trước, nên nó có thể chạy mà không cần phải tạo đối tượng từ lớp Program

Cú pháp khai báo một phương thức cơ bản như sau:

<Access Modifiers> <return type> <name_method>(<parameters>)
{
    // Các câu lệnh trong phương thức
}
  • Access Modifiers cho biết cấp độ được phép truy cập đến hàm này (nói kỹ thi học về hướng đối tượng), có các mức độ như public, private, protect, internal ... Nếu thiếu thành phần này thì mặc định coi là internal (truy cập được ở các file cùng assembly cùng file code). Ngoài ra đếu cho vào từ khóa static ở trước Access Modifiers thì phương thức (hàm) đó gọi là PHƯƠNG THỨC TĨNH (Truy cập được mà không cần tạo đối tượng lớp), thường dùng kèm với Access Modify tên public để tạo các hàm chức năng, tiện ích.
  • return type là kiểu trả về của hàm như int, double, string ... nếu hàm không có kiểu trả về thì đề từ khóa void.
  • name_method tên của phương thức do bạn đặt.
  • parameters là các tham số của hàm nếu có, các tham số khai báo theo mẫu kiểu tên như int thamso1, nhiều tham số thì cách nhau bởi dấu ,.
  • Cuối cùng là phần thân hàm { }, trong đó chứa các câu lệnh C# mà khi hàm được gọi nó sẽ thi hành theo logic của code từ đấu đến cuối, hoặc khi gặp lệnh return;. Khi hàm có kiểu trả về thì trong thân hàm bắt buộc phải có câu lệnh return value; (với value là giá trị, đối tượng, biểu thức có kiểu cùng kiểu tra về của hàm)

Ví dụ 1) Khai báo hàm (phương thức) tĩnh có tên BinhPhuong trong lớp Program, phương thức có một tham số kiểu double, trả về kiểu double là bình phương của tham số. Sau đó gọi hàm tính bình phương của 5.

using System;

namespace CS06_Method
{
    class Program
    {
        static double BinhPhuong(double number)
        {
            double ketqua = number * number;
            return ketqua;  // Hàm kết thúc và trả về giá trị cho hàm
        }
        static void Main(string[] args)
        {
            double bp = BinhPhuong(5); // Gọi hàm
            Console.WriteLine("Bình phương của 5 là: " + bp);
        }
    }
}

Chạy chương trình sẽ in ra:

Bình phương của 5 là: 25

Nhìn vào phần khai báo phương thức BinhPhuong, thấy không chỉ rõ Access Modify nào, điều này có nghĩa hàm có Access Modify mặc định là internal nghĩa là viết đầy đủ thì sẽ là:

static internal double BinhPhuong(double number)

Do có từ khóa static nó trở thành phương thức tĩnh, nên có thể gọi nó mà không cần tạo đối tượng kiểu lớp Program, để gọi hàm dạng này chỉ cần chỉ ra chính xác tên hàm.

Gọi phương thức trong C#

Để gọi, thi hành phương thức ta chỉ việc viết tên đầy đủ của phương thức và truyền vào tham số như ở khai báo.

Ta xem làm sao chỉ đích danh hàm khi gọi: bạn chỉ ra tên đầy đủ như sau namespace.class.method

Vậy để gọi được hàm BinhPhuong cần viết CS06_Method.Program.BinhPhuong(5); tham số truyền cho hàm là 5

Trong hàm Main trên, khi gọi BinhPhuong không cần viết đầy đủ vì mà chỉ cần BinhPhuong(5); chúng cùng namespace nên có thể bỏ namespace, chúng cùng class nên có thể bỏ class.

Tham số trong phương thức

Phương thức có thể khai báo không tham số, một hoặc nhiều tham số. Ví dụ phương thức sau không có tham số.

Tạo file CS06.cs, trong đó khai báo một lớp tên CS06 có phương thức XinChao

using System;
namespace CS06_Method
{
    public class CS06
    {
        public static void XinChao()
        {
            Console.WriteLine("Xin chào C# \n");
        }
    }
}

Quay trở lại phương thức Main, gọi nó 3 lần, kết quả có 3 dòng thông báo hiện thị

CS

Phương thứccó thể khai báo có một hoặc nhiều tham số, mỗi tham số khai bảo chỉ ra kiểu int, float ... và tên của tham số, nếu có nhiều tham số thì cách nhau bởi dấu ,

Ví dụ, phương thức SoLon có hai tham số là số nguyên, trả về là số lớn hơn.

using System;
namespace CS06_Method
{
    public class CS06
    {
        //..
        public static int SoLon(int num1, int num2)
        {
            int max = (num1 > num2) ? num1 : num2;
            return max;
        }
    }
}

Trong Main gọi thử hàm này, với tham số truyền vào hàm viết đúng thứ tự

var max = CS06.SoLon(12, 40);   // gọi  hàm với tham số phù hợp num1 = 12, num2 = 40
Console.WriteLine(max);         // Kết quả: 40

Tham số có giá trị mặc định

Nếu muốn gọi phương thức có các tham số, mà mong muốn tham số đó sẽ nhận giá trị mặc định khi không được truyền giá trị thì khi khai báo tham số khởi tạo ngay cho nó một giá trị mặc định bằng phép gán =

Ví dụ, hàm tính thể tích hình hộp

public static double TheTich(double cao, double dai = 1, double rong = 1)
{
     return cao * dai * rong;
}

Phương thức này có hai tham số nhận giá trị mặc định, bạn có thể gọi phương thức:

double thetich;

thetich = CS06.TheTich(2);              // ~ CS06.TheTich(2,1,1)
Console.WriteLine(thetich);             // 2

thetich = CS06.TheTich(2,10);           // ~ CS06.TheTich(2,10,1)
Console.WriteLine(thetich);             // 20


thetich = CS06.TheTich(1,2,3);
Console.WriteLine(thetich);             // 6

Truyền tham số với tên

Khi gọi phương thức như các ví dụ trên, bạn truyền các tham số bằng cách điền giá trị tham số sau tên phương thức - các tham số viết đúng thứ tự khi khi nó được khai báo.

Trong C# nó còn có cơ chế truyền tham số bằng cách chỉ tên và giá trị khi gọi theo cấu trúc tên:giá-trị nếu có nhiều tham số thì cấu trúc cách nhau bởi ,, điều này giúp cho việc không phải viết đúng thứ tự như khai báo, có tên khi gọi nên có vẻ dễ đọc hơn.

Thử ví dụ sau:

public static string FullName(string ho, string ten, string tendem ="")
{
    return  ho + (tendem != "" ? " " + tendem :"")  + " " + ten;
}

Có thể gọi như cách thông thường

string fullname;

fullname = CS06.FullName("Nguyễn", "A");
Console.WriteLine(fullname);                        //Nguyễn A

fullname = CS06.FullName("ĐINH", "NAM", "HOÀNG");
Console.WriteLine(fullname);                        //ĐINH HOÀNG NAM

Tuy nhiên, giải phóng việc phải viết tham số theo thứ tự, bạn có thể viết:

fullname = CS06.FullName(ten: "A", ho: "Nguyễn");              // Nguyễn A
fullname = CS06.FullName(tendem: "VĂN", ten: "B", ho: "PHẠM"); // PHẠM VĂN B
fullname = CS06.FullName(tendem: "VĂN",ho: "PHẠM",  ten: "B"); // PHẠM VĂN B
fullname = CS06.FullName(ho: "PHẠM", tendem: "VĂN", ten: "B"); // PHẠM VĂN B

Truyền tham số tham chiếu và tham trị C#

Có hai hình thức truyền tham số cho phương thức khi nó được gọi là tham trịtham chiếu

  • tham trị là cách thức mặc định, ta đã sử dụng ở phần trên. Có nghĩa là gán tham số bằng một biến, thì giá trị của biến được copy và sử dụng trong phương thức như biến cục bộ, còn bản thân biến bên ngoài không hề ảnh hưởng.
  • tham chiếu thì bản thân biến ở tham số sẽ được hàm sử dụng trực tiếp (tham chiếu) chứ không tạo ra một biến cục bộ trong hàm, nên nó có tác động trực tiếp đến biến này bên ngoài. Để sử dụng được tham chiếu thì khai báo tham số ở phương thức, cũng như khi gọi cần cho thêm từ khóa ref

Tham số là các đối tượng lớp, mặc định là tham chiếu.

Khai báo ref ở tham số phương thức, bắt buộc khi gọi phải sử dụng biến làm tham số chứ không được dùng giá trị.

Ví dụ tham trị:

public static void ThamChieuThamTri(int x) {
    x = x * x;
    Console.WriteLine(x);
}

Khi sử dụng:

int a = 2;
ThamChieuThamTri(a);
Console.WriteLine(a);
// In ra
// 4
// 2

Ta thấy, biến a bên ngoài phương thức được dùng làm tham số cho phương thức, thì giá trị của a được sao chép và gán vào biến cục bộ x của phương thức, biến x được biến đối nhưng biến a bên ngoài không ảnh hưởng.

Ví dụ tham chiếu:

Sửa lại phương thức trên như sau

public static void ThamChieuThamTri(ref int x) {
    x = x * x;
    Console.WriteLine(x);
}

Khi sử dụng:

int a = 2;
ThamChieuThamTri(ref a);
Console.WriteLine(a);
// In ra
// 4
// 4

Ta thấy a sử dụng làm tham số cho phương thức, tham số x tham chiếu đến a, và khi trong hàm thay đổi x, nghĩa là thay đổi a bên ngoài. (đều in ra 4)

Ngoài ra nếu gọi ThamChieuThamTri(5); thì sẽ lỗi, vì tham chiếu yêu cầu đối số này phải là biến.

Tham chiếu với out

Ngoài dùng từ khóa ref trong khai báo tham chiếu, bạn cũng có thể thay thế nó bằng từ khóa out, điểm khác biệt duy nhất là nếu dùng từ khóa out thì tham số không cần khởi tạo khi truyền cho phương thức, còn dùng ref thì biến làm tham số phải khởi tạo.

public static void OutExample(out int x) {
    x = 100;
}

Khi sử dụng:

int a;             // biến a chưa khởi tạo
OutExample(out a); // Giờ a = 100;

Phương thức Đệ quy

Đệ quy là phương thức khai báo, trong thân của nó có gọi lại chính nó. Ví dụ cổ điển ứng dụng đệ quy là tính giai thừa của một số ví dụ 5! = 5.4.3.2.1

public static int giaithua(int a) {
    if (a == 1)
        return 1; // Kết thúc đệ quy

    return
        a * giaithua (a - 1);  // Đệ quy
}
Console.WriteLine(giaithua(5)); //120

Đệ quy khá giống vòng lặp, bạn cần có điều kiện dừng để tránh đệ quy vô tận.

Hầu hết phương thức đệ quy có thể chuyển qua sử dụng các vòng lặp, nên cũng lưu ý nếu vòng lặp đơn giản thì nên dùng lặp.

Trong nhiều ví dụ test về hiệu năng - thì đệ quy chậm hơn việc sử dụng lặp nhiều. tham khảo

Đăng ký theo dõi ủng hộ kênh