Học tập‎ > ‎C#‎ > ‎

Xây dựng lớp và phương thức trong C#

Không thể tìm thấy URL thông số tiện ích
Người đăng bài : duaca

Xây dựng lớp và phương thức trong C Sharp

Như chúng ta đã biết, C Sharp là ngôn ngữ lập trình hướng đối tượng, chính vì thế việc tạo ra các đối tượng trong 1 chương trình viết bằng C Sharp là điều không thể thiếu; vậy làm thế nào để tạo ra các đối tượng mong muốn khi cần sử dụng chúng trong chương trình của mình? đơn giản thôi ! bạn chỉ việc xây dựng lớp trong chương trình là có thể tạo ra đối tượng bất cứ khi nào cần đến. Vậy lớp là gì ?!, trong tiếng anh, thuật ngữ này có tên là class và việc dịch ra tiếng việt như tôi đã dịch ở trên xem ra có vẻ “thô kệch” và “khó liên tưởng” (Nhất là đối với những bạn mới bắt đầu tiếp xúc với lập trình hướng đối tượng) chính vì thế chúng ta sẽ dùng từ “nguyên bản” : class.

Class có thể được xem như những “khuôn mẫu” được định nghĩa trong chương trình của bạn để “đúc” ra những đối tượng cần thiết; cũng giống như trên thực tế, trong lĩnh vực hàng gia dụng, để “đúc” ra những đôi dép bán cho người tiêu dùng sử dụng, nhà sản xuất sẽ phải chế tạo, thiết kế ra các “khuôn mẫu” cần thiết cho loại dép sẽ sản xuất, sau đó dựa vào nguyên liệu và khuôn mẫu đã chế tạo sẽ sản xuất ra các sản phẩm là những đôi dép cung cấp cho thị trường tiêu dùng.

Trong lập trình hướng đối tượng cũng vậy, bạn cần phải định nghĩa Class như là những khuôn mẫu cần thiết để có thể sản xuất ra các đối tượng sẽ dùng đến cho mục đích nào đó trong chương trình của mình. Trong minh họa ở trên, hạt nhựa chính là nguyên liệu để tạo ra sản phẩm là những đôi dép, trong chương trình của bạn thì nguyên liệu để tạo ra đối tượng chính là dữ liệu. Dữ liệu ở đây có thể là các kiểu dữ liệu cơ bản (Primitive data type) mà C Sharp cung cấp hoặc cũng có thể là những kiểu dữ liệu đặc biệt do bạn tạo ra như Struct, Enum hay những Class khác cần dùng cho việc lưu trữ thông tin trong Class muốn xây dựng.

Liên tưởng

Hãy tưởng tượng !. Bạn sẽ viết 1 chương trình mô tả cho trò chơi đua xe để thỏa mãn “đam mê tốc độ”, như vậy bạn cần phải tạo ra 1 Class có tên là “XeHoi” để ứng với n người chơi, chương trình của bạn sẽ tạo ra n chiếc xe hơi khác nhau phục vụ người chơi sử dụng.

Hoặc giả như bạn sẽ xây dựng 1 ứng dụng phục vụ cho nhu cầu “đấm đá” (Nói vui vậy cho đỡ buồn ngủ), các nhân vật trong chương trình của bạn là những chiến binh cho phép người chơi điều khiển khi tham gia trò chơi – Chắc chắn bạn cũng phải tạo ra 1 Class : “ChienBinh” … và vào thời điểm bắt đầu chơi, người dùng sẽ chọn chiến binh đại diện cho mình, chương trình sẽ thông qua đặc điểm của chiến binh mà người chơi lựa chọn (dưới dạng hình vẽ trong chương trình của bạn cung cấp) để tạo ra đối tượng tương ứng. Dĩ nhiên những chiến binh này tùy thuộc theo ý thích của từng người mà sẽ có những đặc điểm khác nhau, việc nhận dạng và thể hiện các chiến binh khác nhau trong chương trình của bạn chủ yếu dựa vào dữ liệu chính là thông tin mà người chơi lựa chọn và hình ảnh mà bạn cung cấp trong chương trình

street fighter games

Đến đây, chắc bạn đã hiểu về khái niệm Class trong lập trình hướng đối tượng. Vậy khi xây dựng Class trong chương trình, chúng ta phải bắt đầu từ đâu ?. Câu trả lời là không khó, cũng giống như loài người qua hàng ngàn năm tiến hóa đã đẻ ra hàng ngàn phát minh, hầu như tất cả những phát minh này đều là để phục vụ cho cuộc sống và phát triển của con người, nói chung là dựa vào nhu cầu mà có.

Trong lập trình cũng vậy, trước khi xây dựng chương trình, bạn thường phải thiết kế dựa trên những ý tưởng nào đó. Giả dụ trong Game về đua xe, người chơi được phép chọn xe có màu gì, ứng với loại xe đó thì tốc độ di chuyển là bao nhiêu, xe đó phải xử dụng nhiên liệu là xăng, dầu hay gas … Hoặc giả như với game “Đấm đá” ỏ trên, chiến binh của người chơi có thể sẽ có chiều cao, cân nặng, hình dáng như thế nào, và giả sử các chiến binh trong chương trình của bạn đều có khả năng đấm, đá, phi thân khỏi mặt đất thì ứng với mỗi thể trạng của chiến binh mà cú đấm, cú đá ảnh hưởng ra sao đến đối thủ, phi thân có thể cao hay thấp …

Đến đây, thực sự tôi muốn dẫn đắt bạn đến một số vấn đề cơ bản cần có trong class của lập trình hướng đối tượng. Đó chính là properties (thuộc tính) và method (phương thức), mỗi chiếc xe có thể có màu sơn khác nhau, sử dụng nguyên liệu khác nhau : đây chính là các properties cần có trong classxeHoi” ở trên. Mỗi chiến binh có chiều cao, cân nặng, hình dáng khác nhau : đây chính là những properties cần xây dựng trong classchienBinh”.

Mỗi loại xe có thể di chuyển tốc độ khác nhau trên mỗi loại địa hình, mỗi loại xe có ưu thế khác nhau khi vào cua tại những khúc quanh có góc mở khác nhau … tốc độ, khả năng vào cua theo địa hình chính là method cho classxeHoi”. Mỗi chiến binh có cú đá, cú đấm khác nhau hay khả năng của mỗi chiến binh khi phi thân là cao hay thấp, như vậy các khả năng đá, đấm và phi thân được xem như method của classchienBinh”.

Hãy nhớ !. Khi xây dựng class trong C Sharp, bạn phải định nghĩa các properties cùng với method cần thiết để có thể sử dụng cho đối tượng của mình trong chương trình.

Cú pháp tạo class

class <tên_class>{
         <kieu_dl>  <bien_thanh_vien>;
         …
         <phương_thức>(tham_số, …)
}

Theo như cú pháp trên, chúng ta sẽ cùng làm một số ví dụ tạo class theo những yêu cầu dưới đây để hiểu rõ hơn về cú pháp

- VD1:

Định nghĩa 1 class dùng để chứa thông tin về hình tròn trong chương trình với các thông tin lưu trữ mô tả cho các đặc điểm sau của hình tròn : bán kính, màu sắc, hình rỗng hay đặc.

Class hinhTron{
          float  _banKinh;        //--- Bán kính của hình tròn 
          int    _mau;            //--- Màu của hình tròn
          bool   _hinhRong;       //--- Nếu rỗng :true, ngược lại là false
}

- VD2:

Định nghĩa 1 class dùng cho mục đích chứa thông tin về học sinh cần quản lý trong chương trình với các thông tin lưu trữ mô tả cho các đặc điểm sau: mã số học sinh, họ tên, giới tính, số điện thoại, năm sinh.

Class hocSinh{
          int         _maHS;      //--- mã số của học sinh     
          string     _hoTen;      //--- họ tên của học sinh
          bool        _gioiTinh;  //--- Nếu nam :true, ngược lại là false
         
long       _soDT       //--- Số điện thoại cá nhân
         
int         _namSinh    //--- Năm sinh của học sinh
}

- VD3:

Định nghĩa 1 class dùng cho mục đích chứa thông tin của các phân số phục vụ cho việc giải các bài toán trong chương trình với các thông tin lưu trữ: tử số, mẫu số.

Class phanSo{
          int     _tuSo;         //--- mã số của học sinh     
          int     _phanSo;        //--- họ tên của học sinh
}

Qua các ví dụ ở trên, chúng ta mới chỉ đơn thần là tạo ra Class để lưu trữ thông tin dựa vào các biến thành phần đã khai báo. Nếu tạo ra đối tượng của các lớp này thì chúng đơn thuần chỉ dùng để lưu trữ thông tin, điều này cũng giống như 1 người được sinh ra trên thế gian này ngoài việc có thể chứa được thức ăn ở trong bụng thì chẳng biết làm gì cả (có vẻ như hơi vô dụng nhỉ !). Một thành tố gần như không thể thiếu trong mỗi Class được định nghĩa chính là method, method được xem như là khả năng trang bị cho object của Class để nó có thể xử lý những thông tin có liên quan đến mình.

Method

Phương thức, hàm (method) là tập hợp các lệnh dùng để xử lý thông tin cho 1 mục đích nào đó, thông thường phương thức được tạo ra và được đặt tên để có thể gọi sử dụng thông qua đối tượng của Class. Cú pháp để định nghĩa 1 phương thức được mô tả như sau:

<access_modifier>  <return_type>  <Method_Name> ([list_of_parameter]){
         …
         <lệnh> ;
         …
}

Trong đó :

<access_modifier> : chính là phạm vi (còn gọi là tầm vực) truy xuất của phương thức trong chương trình.

<return_type> : là kiểu dữ liệu mà sau khi thi hành, hàm sẽ trả về giá trị thuộc kiểu đã khai báo cho nơi gọi

<method_name> : tên của phương thức, hàm. Tên của phương thức khi đặt phải tuân theo quy ước về đặt tên cho danh định trong chương trình.

[list_of_parameter] : chính là danh sách các tham số tượng trưng cho dữ liệu sẽ truyền vào trong hàm để xử lý khi gọi nó.

Lưu ý: Tên của hàm khi đặt phải tuân theo các yêu cầu sau:

-          Không được trùng với từ khóa thuộc ngôn ngữ C Sharp

-          Không được có khoảng trắng

-          Không được bắt đầu bởi ký số

-          Chỉ có thể bắt đầu bởi ký tự, dấu gạch dưới hoặc dấu @

Bảng mô tả một số tình huống đặt tên method

Tên method

Diễn giải đối với tên đặt sai

@them()

 

bool()

Sai vì trùng tên với từ khóa trong C Sharp

Di chuyen(int x, int y)

Sai vì tên hàm có khoảng trắng

cong_Hai_So(float a, float b)

 

_tinhHoanhDo()

 

5lan(int x)

Sai vì tên hàm này bắt đầu bởi ký số

 

Lý thuyết là chuẩn mực và mang tính tổng quát, chính vì thế có thể bạn sẽ thấy cú pháp hơi rắc rối. Đừng lo, chúng ta sẽ cùng làm một số ví dụ về việc định nghĩa phương thức, bạn sẽ thấy nó rất đơn giản.

Vd 1:

          Hãy định nghĩa trong Class hinhTron 1 phương thức cho phép tính và trả về chu vi của hình tròn

public  float  chuVi(){
    return  2 * 3.14 * _banKinh;
}

Vd 2:

          Hãy định nghĩa trong Class hocSinh 1 phương thức cho phép xuất thông tin đầy đủ của đối tượng học sinh

public  void  xuatThongTin(){
    console.WriteLine(“Mã học sinh : {0}”,_maHS);
    console.WriteLine(“Họ và tên : {0}”,_hoTen);
    console.WriteLine(“Giới tính : {0}”, (_gioiTinh==true?”Nam”:”Nữ”);
    console.WriteLine(“Số điện thoại : {0}”,_soDT);
    console.WriteLine(“Năm sinh : {0}”,_namSinh);
}

Vd 3:

          Hãy định nghĩa trong Class phanSo 1 phương thức cho phép xuất ra thông tin của đối tượng phân số hiện hành

public  void  inPhanSo(){
    console.WriteLine(“ {0} / {1}”, _tuSo, _mauSo);
}

Tạo và sử dụng đối tượng

Như đã giới thiệu trong phần trước, đối tượng (Object) chính là 1 thể hiện của Class chính vì thế trong C Sharp, muốn tạo 1 đối tượng của 1 Class đã định nghĩa trong chương trình, bạn chỉ việc sử dụng từ khóa new theo cú pháp như sau:

<tên_Class>  <ten_Object>  =  new  <ten_Class>();

Ví dụ: để tạo ra 1 Object của Class hình tròn đã định nghĩa, bạn chỉ việc dùng lệnh như sau

hinhTron  h1 = new  hinhTron();

Để tạo 1 Object của Class hocSinh ta có thể dùng lệnh theo của pháp như thế này

hocSinh  hs = new hocSinh();

Khi đã có Object của Class, để gọi Method đã định nghĩa trong Class bạn chỉ việc gọi thông qua đối tượng của Class là Method đó sẽ thực thi những câu lệnh mà bạn đã sắp đặt.

Ví dụ 1: để tính chu vi của Object h1 và xuất ra màn hình tôi sẽ sử dụng lệnh như sau

 float ketQua = h1.chuVi();
 console.WriteLine(“Chu  vi tính được = {0}”, ketQua);

Ghi chú: trong ví dụ trên, chắc bạn sẽ rất thắc mắc vậy chu vi tính được sẽ in ra màn hình là bao nhiêu, trong khi trong method chuVi chúng ta đã dùng công thức “2 * 3.14*_banKinh” và trong đó _banKinh chưa biết có giá trị nào. Bạn cứ yên tâm, chúng ta còn cần phải có thêm 1 số kiến thức nữa thì việc mô tả code sẽ tường minh hơn, tạm thời bạn cứ chấp nhận như vậy, biến _banKinh bằng bao nhiêu thì sẽ được tính đúng với giá trị mà nó đang có theo công thức mà chúng ta đã sử dụng

Ví dụ 2: để in ra thông tin của Object hs tôi sẽ sử dụng lệnh như sau

hs.xuatThongTin();

Như vậy, chúng ta thấy rằng trong lập trình hướng đối tượng, nếu muốn gọi thực thi 1 hàm, 1 phương thức nào đó thì bắt buộc bạn phải có 1 thể hiện của Class và lúc này chúng ta có thể gọi hàm thông qua Object của Class đó.

Tuy nhiên có 1 trường hợp đặc biệt cho phép bạn có thể gọi hàm mà không cần thông qua đối tượng, trường hợp này chúng ta sẽ đề cập đến static method ở phía sau.

Static methods

Như vừa đề cập đến ở trên, mặc nhiên trong C Sharp thì một phương thức chỉ có thể gọi thông qua Object thuộc Class và điều này sẽ trở nên khó khăn khi lập trình viên muốn xây dựng trong chương trình 1 phương thức để dùng chung cho mọi tình huống

Ví dụ: bạn cần viết 1 phương thức để nạp thông tin từ 1 tập tin ở trên đĩa, nếu như theo cách trên thì ứng với tất cả các Class trong chương trình có nhu cầu nạp thông tin từ File cho Object của nó, bạn phải xây dựng tất cả các phương thức nạp File cần thiết … và điều này có vẻ như hơi lãng phí, giống như làm khó cho người viết code vậy mặc dù bạn có thể sao chép Code của phương thức cần thiết đến nhiều nơi nhưng điều này cũng vẫn là trở ngại lớn cho lập trình viên nhất là khi cần phải điều chỉnh lại giải thuật cho những đoạn code này

Để giải quyết sự bất cập này, C Sharp cung cấp cho bạn 1 cách để định nghĩa Method để khi cần, có thể gọi sử dụng những Method đó 1 cách thuận tiện mà không cần phải thông qua Object; đó chính là Static method.

Việc định nghĩa static method trong chương trình chẳng có nhiều khác biệt so với việc định nghĩa method thông thường, bạn chỉ việc thêm vào trước phần khai báo method từ khóa static và <access_modifier> phải sử dụng là public là đủ (Chúng ta sẽ làm quen với ý nghĩa của access_modifier ở phí sau).

Cú pháp

static <access_modifier>  <return_type>  <Method_Name>                                                      ([list_of_parameter])
{
         …
         <lệnh> ;
         …
}

Ví dụ: Chúng ta cần viết 1 hàm để tính bình phương của 1 số và hàm này có thể gọi ở bất cứ đâu trong chương trình mà chẳng cần phải thông qua  Object.

static  long  binhPhuong(long so){
    return so*so;
}

Vậy với static method, chúng ta sẽ gọi sử dụng ra sao ? C Sharp cho phép bạn gọi trực tiếp thông qua tên của Class có chứa nó

<class_name>.<static_method>(parameter, …);

Ví dụ : Chúng ta sẽ viết 1 chương trình cho phép tính diện tích và chu vi của các hình tròn, hình vuông. Trong đó ta sẽ xây dựng  phương thức tĩnh binhPhuong để tính bình phương cho các số truyền vào khi gọi hàm này. Chương trình được viết như sau:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace tinhToanCacHinh
{
    class tinhToan{
        public static float binhPhuong(float a){
            return a * a;
        }
    }
    class hinhTron{
        float _banKinh;
        /// <summary>
        /// Hàm khởi tạo 1 tham số của lớp hình tròn
        /// </summary>
        /// <param name="gt"></param>
        public hinhTron(float gt){
            _banKinh = gt;
        }
        public float dienTich(){
            return tinhToan.binhPhuong(_banKinh);
        }
    }
    class hinhVuong{
        long _canh;
        /// <summary>
        /// Hàm khởi tạo 1 tham số của lớp hình vuong
        /// </summary>
        /// <param name="gt"></param>
        public hinhVuong(long gt){
            _canh = gt;
        }
        public float dienTich(){
            return tinhToan.binhPhuong(_canh);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            hinhTron h1 = new hinhTron(5);
            hinhVuong h2 = new hinhVuong(9);
            Console.WriteLine("Diện tích hình tròn {0}", h1.dienTich());
            Console.WriteLine("Diện tích hình vuông {0}", h2.dienTich());
            Console.ReadLine();
        }
    }
}

Ghi chú:

-          Tôi tạo 1 project có tên tinhToanCacHinh trong visual Studio, và mặc nhiên thì VS tự động cung cấp cho tôi 1 nameSpace trùng tên với tên của project và tự động tạo ra 1 class chính cho project này với tên là program.cs

-          Bạn thấy trong tập tin có tên trùng tên với Class chính của chương trình là program.cs, tôi tạo ra 3 Class khác có tên là tinhToan, hinhVuong, hinhTron. Thông thường thì trong C Sharp cứ ứng với 1 Class được tạo ra trong chương trình sẽ được lưu trữ trên đĩa trong 1 file trùng tên với tên Class cần tạo. Tuy nhiên bạn vẫn có thể chứa nhiều Class trong 1 tập tin (như cách làm ở trên) và như vậy thì người ta thường gọi bằng thuật ngữ “sub Class”, điều này là hợp lệ, tuy nhiên sau này nếu bạn đã trở thành lập trình viên, bạn nên hạn chế việc xây dựng “sub Class” giống như ví dụ của tôi trong chương trình của mình, sở dĩ trong ví dụ tôi làm như thế để giúp bạn nhìn code đỡ rắc rối hơn mà thôi.

-          Trong các method dùng để tính diện tích của Class hinhTron và Class hinhVuong tôi đã truy xuất đến Static method có tên binhPhuong thông qua tên của Class có chứa nó theo như cú pháp đã mô tả : tinhToan.binhPhuong(_canh)

Static varialbe

Ứng với phương thức tĩnh (static method), C Sharp cho phép bạn có thể định nghĩa các biến thành viên thuộc dạng static để sử dụng và thường thì biến static được dùng cho mục đích chia sẻ thông tin của Class đối với tất cả các Object thuộc về Class tương ứng. Ví dụ tại một thời điểm nào đó bạn cần theo dõi trong chương trình có bao nhiêu đối tượng của Class X đã được tạo ra, trong tình huống này thì việc sử dụng 1 static variable là hợp lý nhất.

Access modifiers

Thuật ngữ này thường được dịch ra trong 1 số sách dạy lập trình bằng tiếng việt là bổ từ, vậy Access modifiers có ý nghĩa như thế nào ? cách sử dụng ra sao ?. Trong lập trình hướng đối tượng, Access modifiers là thuật ngữ dùng để ám chỉ một số từ khóa đặc biệt dùng để khai báo cho Class, Method hay Variable nhằm chỉ ra phạm vi sử dụng cũng như mức độ ảnh hưởng của nó trong chương trình của bạn, tôi tạm gọi là tầm vực của đối tượng sử dụng nó để khai báo (tầm vực : tầm ảnh hưởng, phạm vi hay khu vực mà đối tượng được khai báo có thể tác động đến). Những Access modifiers mà bạn có thể sử dụng cho việc khai báo trong chương trình của mình bao gồm:

-          public : dùng để khai báo những đối tượng mang tính “công cộng”, có thể truy xuất hay gọi sử dụng từ bên ngoài Class, Assembly hay Namespace

-          private : là từ khóa để chỉ ra rằng đối tượng mà bạn khai báo là riêng tư, chỉ có thể sử dụng bên trong nội tại của nơi mà nó khai báo mà thôi.

-          protected : từ khóa này thường cũng tương tự như private, tuy niên phạm vi ảnh hường của nó lớn hơn private và có liên quan đến thừa kế trong lập trình hướng đối tượng (tôi sẽ lý giải về từ khóa này kỹ hơn trong bài viết nói về thừa kế, bạn hãy chờ nhé)

-          internal : đây là từ khóa cho phép khai báo để cho phép truy xuất trong phạm vi các lớp thuộc cùng Assembly của chương trình.

Có lẽ tới đây, bạn cảm thấy các lý giải ở trên dường như rất mơ hồ !, tôi xin giúp bạn liên tưởng thế này : trong nhà bạn, phòng khách là nơi mà mọi thành viên trong gia đình đều có thể sử dụng nó như nhau => khi xây dựng, phòng khách đã được khai báo là “public”. Trong phòng của bạn, những đồ vật như bàn ghế, chăn mền, … chỉ dành cho bạn sử dụng mà thôi, các thành viên khác không được phép => bàn ghế, chăn mền … trong phòng của bạn đã được khai báo là “private” (cái bóp trong túi bạn cũng là private, mọi người xung quanh đều không có quyền lục bóp của bạn để lấy tiền đấy !!!). Protected thì thế này, ba mẹ bạn mua 1 chiếc xe SH làm phương tiện đi lại, khi cần thì bạn hay anh chị em trong gia đình đều có thể sử dụng chiếc xe đó, … nhưng anh bạn hàng xóm của bạn thì không được phép sử dụng chiếc SH đó, có thể nói chiếc SH của gia đình bạn đã được khai báo là protected. Internal thì lại khác; trong 1 căn phòng, mọi đồ vật như bàn ghế, người, tủ, … đều có thể nhận ánh sách từ chiếc bóng đèn treo trên trần nhà, trong tình huống này thì mọi đồ vật và người trong căn phòng được xem như là các Class khác nhau và Assembly chính là căn phòng thì bóng đèn gắn trên trần nhà được xem như là đã được khai báo Access modifier với từ khóa internal rồi đó.

Bảng mô tả tầm ảnh hưởng của Access Modifiers

 

Có thể truy xuất trong phạm vi chương trình

Có thể truy xuất trong phạm vi của lớp chứa khai báo

Có thể truy xuất từ các lớp thừa kế của lớp chứa khai báo

public

þ

þ

þ

private

ý

þ

ý

protected

ý

þ

þ

internal

ý

þ

þ

 

Vấn đề truyền tham số cho hàm trong C Sharp

Như cú pháp khai báo dành cho method đã đề cập ở mục trước, ta thấy method có thể có tham số truyền vào hoặc không có. Vậy chính xác tham số là gì ?, tại sao phải có tham số ? Nếu không có tham số cũng được thì khi xậy dựng method tôi không dùng tham số cho tất cả các method được không ?. Tôi xin diễn giải để trả lời các câu hỏi trên thông qua 1 số ý như sau

-          Theo định nghĩa, tham số là tập hợp các dữ liệu cần có để cung cấp cho hàm xử lý tại thời điểm mà hàm được gọi. Ví dụ như việc sử dụng xe gắn máy để di chuyển, hôm nay tôi chỉ có tiền để đổ vào bình 1 lít xăng, như thế trong ngày hôm nay tôi chỉ có thể dùng xe gắn máy di chuyển trong phạm vi tối đa là 50 km (giả sử 1 lít xăng thì xe của tôi có thể di chuyển trong phạm vi 50Km), hôm qua tôi có thể đổ được 3 lít, có nghĩa là ngày hôm qua tôi có thể dùng xe gắn máy di chuyển được tối đa 150 Km; hoặc giả là ngày mai, tôi cần phải di chuyển trong phạm vi 250Km thì trước khi di chuyển tôi phải đổ vào bình xăng 5 lít. Trong tình huống này, tôi muốn bạn liên tưởng đến chiếc xe của mình như là phương thức trong lập trình, lượng xăng đổ vào bình chính là tham số cung cấp cho phương thức đó và ứng với dữ liệu cung cấp khác nhau (lượng xăng đổ vào bình) thì khả năng hoạt động của hàm để đưa ra kết quả cũng sẽ khác nhau.

-          Như vậy việc có hay không có tham số còn tùy thuộc vào mục tiên hoạt động và nhu cầu sử dụng của method tương ứng, nhớ lại 3 method mà tôi đã minh họa trong phần trước : chuVi(); xuatThongTin(); inPhanSo() tôi đều không sử dụng tham số vì những hàm này khi hoạt động dựa trên dữ liệu nội tại của đối tượng. Trong khi đó hàm binhPhuong(long so) có 1 tham số và tùy theo số truyền vào mỗi thời điểm mà hàm này sẽ tính và trả về giá trị cho thích hợp. Rõ ràng việc có hay không có tham số còn tùy thuộc vào mục tiêu hoạt động của hàm, tuy nhiên việc sử dụng tham số sẽ làm cho khả năng xử lý thông tin của hàm được linh hoạt hơn (Nhưng bạn cũng không nên quá lạm dụng tham số cho hàm vì đôi khi sẽ trở thành mình tự làm khó chính mình)

Trong C Sharp, tham số truyền cho hàm theo cách mặc nhiên được gọi là truyền tham trị (Tham số truyền vào như là những giá trị cung cấp cho hàm). Hãy khảo sát 1 ví dụ sau

private  float  tinhToan(float a, float b){
   float c = 0;
   a ++;
   b --;
   c = a – b;
   return c;
}

Về thuật toán thì hàm này chẳng có ý nghĩa gì đặc biệt, tôi chỉ đơn thuần dùng nó để mô tả cho ý nghĩa tham trị của các tham số a và b mà thôi.

Như bạn thấy, khi hàm này được gọi, nó sẽ thực hiện lần lượt các công việc sau

+ khai báo và khởi gán biến c
+ tăng giá trị của tham số a
+ giảm giá trị của b
+ tính hiệu số giữa a và b và trả về kết quả tính được cho nơi gọi hàm

Sau khi tìm hiểu đến đây, bạn vẫn chẳng thấy việc mô tả cho ý nghĩa truyền tham trị là nằm ở đâu. Đúng thế, mới chỉ có nội dung của hàm vẫn chưa thể làm rõ được ý nghĩa của tham trị sử dụng, bây giờ tôi thêm 1 đoạn code để mô tả cho việc gọi sử dụng hàm này từ bên trong 1 hàm khác như sau

float s1 = 7;
float s2 = 3;
float s3 = tinhToan(s1,s2);
console.WriteLine(“Số thứ nhất : {0}”,s1);
console.WriteLine(“Số thứ hai : {0}”,s2);
console.WriteLine(“Kết quả = {0}”,s3);

Theo bạn, sau khi thi hành kết quả sẽ in ra trên màn hình thế nào ?. Kết quả như sau

Số thứ nhất: 7
Số thứ hai: 3
Kết quả = 6

Có lẽ bạn sẽ cảm thấy hơi lạ phải không ? rõ ràng tại vị trí của tham số thứ nhất truyền vào, bên trong hàm sẽ tăng giá trị của nó lên 1 và giảm giá trị của tham số thứ 2 xuống 1 nhưng sao in ra vẫn là 7 và 3 nhỉ ?. Câu trả lời nằm ở đây, theo như mặc định, truyền tham số như ví dụ minh họa này thì sẽ được xem như cách truyền tham trị, việc tính toán trong hàm không làm ảnh hưởng tới giá trị của biến tại nơi gọi hàm

Trong những tình huống nhất định nào đó, lập trình viên mong muốn việc tính toán bên trong hàm cần phải làm thay đổi giá trị và ảnh hưởng đến biến sử dụng làm tham số cho hàm tại thời điểm mà hàm được gọi. Để đáp ứng điều này, C Sharp cung cấp cách truyền dạng tham chiếu (reference) để sử dụng cho tham số của hàm thông qua các từ khóa “ref” và “out”.

Về nguyên tắc thì 2 từ khóa này dùng để khai báo cho các tham số thuộc hàm nhằm ám chỉ những tham số này được truyền dưới dạng tham chiếu, trong tình huống này thay vì truyền giá trị vào cho hàm, địa chỉ của ô nhớ chứa biến giá trị được truyền vào để chương trình lấy dữ liệu từ ô nhớ tương ứng ra tính toán đồng thời thay đổi giá trị (nếu có) của chính ô nhớ đó, chính vì thế sau khi thi hành phương thức, giá trị ô nhớ ban đầu rất có thể bị biến đổi do các lệnh trong hàm tác động vào.

Cách sử dụng từ khóa “ref

private  float  tinhToan(ref float a, ref float b){
   float c = 0;
   a ++;
   b --;
   c = a – b;
   return c;
}

Và lúc này, trong 1 hàm khác nếu bạn gọi sử dụng hàm tính toán thì sẽ gọi hàm theo cách như sau

float s1 = 7;
float s2 = 3;
float s3 = tinhToan(ref s1,ref s2);
console.WriteLine(“Số thứ nhất : {0}”,s1);
console.WriteLine(“Số thứ hai : {0}”,s2);
console.WriteLine(“Kết quả = {0}”,s3);

Bây giờ, sau khi thi hành kết quả sẽ in ra trên màn hình như sau

Số thứ nhất: 8
Số thứ hai: 2
Kết quả = 6

Trong tình huống này, bạn thấy tại nơi gọi hàm tinhToan, từ khóa ref cũng phải truyền vào cùng với biến dùng cho tham số tại vị trí tương ứng.

Việc sử dụng từ khóa out cho mục đích truyền tham số đối với tham số của hàm cũng giống như sử dụng từ khóa ref ở trên, khác biệt duy nhất là tham chiếu truyền bằng từ khóa ref thì bạn phải khởi gán giá trị trước khi truyền vào (Đây là quy ước mặc nhiên của C Sharptất cả các biến trong chương trình đều phải khởi tạo trước khi sử dụng) còn với từ khóa out thì ngược lại, biến truyền vào theo hình thức này phải là 1 biến nguyên thủy sau khi khai báo (tức là khai báo nhưng không khởi tạo giá trị cho biến)

Cách sử dụng từ khóa “out

private  float  bienDoi(out float x, out float y){
   float z = 0;
   x =5;
   y = (x + 2);
   z = (x + y);
   return z;
}

Và lúc này, trong 1 hàm khác nếu bạn gọi sử dụng hàm tính toán thì sẽ gọi hàm theo cách như sau

float s1;
float s2;
float s3 = bienDoi(out s1,out s2);
console.WriteLine(“Số thứ nhất : {0}”,s1);
console.WriteLine(“Số thứ hai : {0}”,s2);
console.WriteLine(“Kết quả = {0}”,s3);

Bây giờ, sau khi thi hành kết quả sẽ in ra kết quả như sau

Số thứ nhất: 5
Số thứ hai: 7
Kết quả = 12

Kỹ thuật overloading

Đôi khi trong quá trình xây dựng ứng dụng, bạn cần phải xây dựng hàm cho một mục đích nào đó nhưng lại phải xử lý cho quá nhiều tình huống khác nhau; ví dụ như thế này: ta cần xây dựng 1 hàm cho phép cộng hai số với nhau, tuy nhiên trong các tình huống sử dụng khác nhau thì kiểu dữ liệu của tham số truyền vào cũng có thể sẽ khác nhau và như thế bạn phải xây dựng rất nhiều hàm với nhiều tên gọi cho cùng 1 mục đích là cộng 2 số; Điều này đôi khi lại gây ra những phiền toái không đáng có cho người lập trình (Phải nhớ quá nhiều tên hàm chỉ cho 1 vấn đề khi sử dụng).

Kỹ thuật Overloading chính là cách cho phép lập trình viên khắc phục vấn đề trên, bạn có thể định nghĩa nhiều hàm với cùng 1 tên gọi và khi sử dụng, bạn chỉ việc gọi tên của hàm giống như thông thường và truyền tham số cho hàm, lúc này trình biên dịch sẽ tự xác định hàm cần thiết cho lời gọi hàm tương ứng. Điều này rất thuận tiện về mặt ý nghĩa sử dụng hàm trong chương trình của chúng ta.

Để minh họa cho vấn đề này, chúng ta cùng xây dựng 1 hàm dùng cho mục đích tính bình phương của 1 số, hàm của chúng ta có thể xử lý cho tất cả các số thuộc những kiểu sau : int, long, float, double (Bạn có thể thử nghiệm cho tất cả các kiểu dữ liệu số của C Sharp, tôi chỉ chọn 4 kiểu tiêu biểu cho đỡ dài dòng)

public int  binhPhuong(int so){
   return so*so;
}
public long  binhPhuong(long so){
   return so*so;
}
public float  binhPhuong(float so){
   return so*so;
}
public double  binhPhuong(double so){
   return so*so;
}

và trong 1 hàm khác thuộc lớp hiện hành, tôi gọi sử dụng hàm bình phương cho 2 kiểu dữ liệu long và float như sau

long a = 5;
float b = 9;
long kq1 = binhPhuong(a);
float kq2 = binhPhuong(b);
console.WriteLine(“{0} bình phương = {1}”,a, kq1);
console.WriteLine(“{0} bình phương = {1}”,b, kq2);

Một số lưu ý khi sử dụng kỹ thuật Overloading

Do đặc điểm cho phép dùng trùng tên hàm trong cùng phạm vi trong chương trình, chính vì thế bắt buộc các hàm của bạn phải có sự khác biệt để trình biên dịch có thể nhận dạng chính xác và thực thi đúng với yêu cầu tại thời điểm mà hàm được gọi. Sự khác biệt đó phải thể hiện ở 1 trong 2 tình huống sau:

-          Khác về kiểu dữ liệu của tham số (Giống như hàm binhPhuong vừa minh họa ở trên)

-          Khác về số lượng tham số sử dụng để truyền vào cho hàm

Constructor

Constructor dịch theo ngữ nghĩa tiếng việt còn được gọi là hàm khởi tạo, đây là hàm đặc biệt của Class sẽ tự động thực thi mã lệnh chứa bên trong nó vào thời điểm mà đối tượng của Class được tạo ra, thông thường lập trình viên sử dụng Constructor để khởi tạo các giá trị cần thiết  đối với các biến thành viên thuộc Class tương ứng. Ngoài đặc điểm này, constructor còn có một số đặc điểm khác như sau:

-          Có tên trùng với tên Class mà nó thuộc về

-          Không có kiểu trả về dành cho hàm đồng thời cũng không được sử dụng từ khóa void trong khai báo

-          Luôn luôn được khai báo với access modifier là public

-          Không thể gọi Constructor thực thi giống như cách thông thường

Default constructor

Còn được gọi là hàm khởi tạo mặc nhiên của Class, nếu bạn tạo ra Class mà không cung cấp bất cứ 1 hàm khởi tạo nào thì trình biên dịch sẽ tự động tạo ra hàm này và khởi gán giá trị 0 cho tất cả các biến thành viên có kiểu dữ liệu số thuộc Class tương ứng

Static constructor

Hàm khởi tạo Static thường được dùng để khởi gán giá trị cho các biến thành viên thuộc loại Static của Class. Static constructor cũng là 1 dạng hàm đặc biệt của Class, do đó nó có một số đặc điểm sau:

-          Không cho phép sử dụng bất kỳ tham số nào để truyền cho hàm

-          Không được sử dụng Access modifier để khai báo bởi vì Static constructor được gọi thực thi trực tiếp bởi CRL (Common Language Runtime) chứ không phải bởi đối tượng của Class

-          Các lệnh bên trong Static constructor không được phép truy xuất đến bất cứ biến thành viên không phải là static của Class

Cú pháp

Trong quá trình xây dựng Class, C Sharp cho phép bạn cũng có thể sử dụng kỹ thuật Overloading đối với Constructor. Có nghĩa là bạn cũng có thể tạo ra nhiều constructor cho Class, tuy nhiên bạn luôn phải tuân thủ theo những nguyên tắc về Overloading đã đề cập ở trên

Những vấn đề khác

Destructor

Cũng giống như constructor, destructor cũng là 1 hàm đặc biệt của Class, nó tự động được gọi vào thời điểm mà đối tượng của Class bị hủy bỏ. Về ý nghĩa thì destructor thường được dùng để thực thi các lệnh mang tính chất “dọn dẹp” khi đối tượng của Class không còn được dùng đến nữa.

Một số đặc điểm chính của destructor như sau :

-          Luôn có ký tự đầu tiên là “~” kết hợp với tên trùng với tên của Class

-          Không thể sử dụng kỹ thuật Overloading cho destructor

-          Không thể gọi thi hành (Nó tự động được gọi khi đối tượn của Class không còn được dùng đến trong chương trình)

-          Không sử dụng các từ khóa loại Access modifier để khai báo

-          Không được phép sử dụng tham số cho destructor

Garbage collector

Garbage collector chính là cơ chế “dọn dẹp” được cung cấp trong C Sharp với mục đích thu hồi vùng nhớ đối với những đối tượng không còn được sử dụng đến trong chương trình. Khi 1 đối tượng trong chương trình trong 1 thời gian dài không được gọi sử dụng hay chương trình đã thoát ra khỏi phạm vi mà trước đó đã khai báo và khởi tạo đối tượng thì Garbage collector sẽ thực thi các lệnh cần thiết để giải phóng vùng nhớ, giành không gian trống cho những đối tượng khác được tạo ra trong chương trình.

Để dễ hình dung, bạn có thể tưởng tượng Garbage collector cũng giống như công ty vệ sinh của thành phố vậy, vào cuối ngày, nhân viên và xe rác của công ty sẽ tự động thu gom rác trong thành phố để  trả lại không gian cũng như môi trường trong sạch cho người dân bắt đầu 1 ngày mới sau khi thức giấc.

Từ khóa “this

Từ khóa này dùng để ám chỉ cho đối tượng hiện hành của Class, việc sử dụng từ khóa này khi truy xuất các biến thành viên của Class thông thường sẽ làm cho mã lệnh của chương trình tường minh hơn đồng thời giúp cho lập trình viên có thể tránh được việc đụng độ về tên khi vô tình tên của tham số truyền vào cho hàm và tên biến thành viên bị trùng nhau

Tuy nhiên có 1 điểm mà bạn cần phải nhớ, đó là không được phép sử dụng từ khóa this để truy xuất đến các biến thành viên thuộc loại static của Class.

Ví dụ sau minh họa việc sử dụng từ khóa this để truy xuất đến các biến thành viên của Class

Đũa cả

www.bodua.com

Tài liệu tham khảo : Giáo trình Aptech

Comments