Interface trong C#

Nguyễn Dương 07-06-2024

1. INTERFACE LÀ GÌ?

Các bạn đã biết, C# không hỗ trợ đa kế thừa, 1 lớp kế thừa từ nhiều lớp. Tại sao lại như vậy, ta xét thử 2 trường hợp kế thừa dưới đây.

Giả sử ta có 4 lớp A, B, C, D. 
Đa kế thừa



Trường hợp 1: Lớp B kế thừa từ lớp A, lớp C kế thừa từ lớp B, lớp D kế thừa từ lớp C nhưng lớp A lại kế thừa từ lớp D
Trường hợp 2: Lớp B và D kế thừa từ lớp A, lớp C kế thừa từ lớp D, lớp B kế thừa từ lớp C.

Bạn thử nghĩ xem trong 2 trường hợp này thì luồng đi dữ liệu kiểu gì, thừa hưởng thuộc tính và phương thức kiểu gì, xử lý ra sao. 

Về lý thuyết thì ngôn ngữ lập trình không cần hạn chế điều này, tất cả do lập trình viên nhưng quan điểm của Microsoft thì họ muốn lập trình với C# phải tường minh, hạn chế lỗi.

Nhưng nếu không hỗ trợ đa kế thừa thì có bị hạn chế gì không, đa kế thừa giúp ích gì. Ta xét thử ví dụ sau.
Giả sử mình có 2 lớp 
- Động vật trên cạn 
- Động vật dưới nước 

Tiếp theo mình tạo 1 lớp Ếch. Vì ếch là loài lưỡng cư nên nó phải được thừa hưởng thuộc tính và phương thức của cả 2 lớp Động vật trên cạn và Động vật dưới nước. Nhưng C# không hỗ trợ đa kế thừa, vì vậy Interface ra đời như một giải pháp thay thế.

Interface được hiểu như là 1 khuôn mẫu chung mà mọi lớp thực thi nó đều phải tuân theo. Bên trong Interface là 1 tập hợp các thành phần chỉ có khai báo mà không có phần định nghĩa.

Các thành phần đó bao gồm:
- Phương thức
- Property 
- Event
- Indexers 

Đặc điểm của Interface
- Chỉ chứa khai báo không chứa phần định nghĩa.
- Việc ghi đè 1 thành phần trong interface cũng không cần từ khoá override.
- Không thể khai báo phạm vi truy cập cho các thành phần bên trong interface. Các thành phần này sẽ mặc định là public.
Interface không chứa các thuộc tính (các biến) dù là hằng số hay biến tĩnh vẫn không được.
Interface không có hàm khởi tạo và hàm hủy.
- Các lớp có thể thực thi nhiều interface cùng lúc, đây là giải pháp thay thế đa kế thừa.
- Một interface có thể kế thừa nhiều interface khác nhưng không thể kế thừa bất kỳ lớp nào.


2. KHAI BÁO VÀ SỬ DỤNG INTERFACE

Khai báo:
interface <tên interface>
{
        // Các thành phần bên trong
}

Lưu ý: Để dễ phân biệt, các lập trình viên thường thêm chữ "I" vào đằng trước tên interface. Ví dụ IShape, IAnimal ......

Sử dụng:

Ví dụ: Lớp ếch kế thừa 2 interface là động vật trên cạn và động vật dưới nước.

namespace Vi_du
{
    interface IDongVatTrenCan
    {
        void Jump();
    }
    interface IDongVatDuoiNuoc
    {
        void Swim();
    }

    class Ech : IDongVatTrenCan, IDongVatDuoiNuoc
    {
        public void Jump()
        {
            Console.WriteLine("Ech nhay cao 20 cm");
        }
        public void Swim()
        {
            Console.WriteLine("Ech boi rat xa");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Ech E1 = new Ech();
            E1.Jump();
            E1.Swim();

            Console.ReadLine();
        }
    }
}

Kết quả

Vì việc thực thi interface rất giống với kế thừa nên ta hoàn toàn có thể sử dụng câu lệnh sau:
IDongVatTrenCan E1 = new Ech();
E1.Jump();

Kết quả chỉ in ra màn 1 dòng chữ: Ech nhay cao 20 cm. Vì lệnh trên là tạo E1 thừa hưởng interface động vật trên cạn nên chỉ gọi được hàm Jump.


3. XỬ LÝ KHI TRÙNG TÊN HÀM

Xét ví dụ trên, giả sử 2 hàm trong 2 interface động vật trên cạn và động vật dưới nước, mình không đặt tên là Jump và Swim nữa, mà đặt tên chung là Move. Vậy làm sao để chương trình biết là đang gọi hàm Move của interface nào. 
Để giải quyết trường hợp này, ta làm như sau: 
- Loại bỏ public khỏi hàm Move
- Sử dụng từ khóa as để chỉ rõ đang gọi hàm Move của interface nào

Ví dụ:
namespace Vi_du
{
    interface ILandAnimal
    {
        void Move();
    }
    interface IWaterAnimal
    {
        void Move();
    }

    class Frog : ILandAnimal, IWaterAnimal
    {
        void ILandAnimal.Move()
        {
            Console.WriteLine("Ech nhay cao 20 cm");
        }
        void IWaterAnimal.Move()
        {
            Console.WriteLine("Ech boi rat xa");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Frog E1 = new Frog();
            (E1 as ILandAnimal).Move();
            (E1 as IWaterAnimal).Move();

            Console.ReadLine();
        }
    }
}

Kết quả vẫn ra như lúc đầu.


4. SO SÁNH INTERFACE VÀ ABSTRACT

 Giống nhau:
- Đều có thể chứa phương thức trừu tượng
- Đều không thể khởi tạo đối tượng

Khác nhau:
interface
abstract class
Chỉ có thể kế thừa từ interface khác
Có thể kế thừa từ 1 lớp hoặc nhiều interface
Chỉ chứa khai báo, không có nội dung
Có thể chứa các thuộc tính và phương thức như class bình thường
Không sử dụng override để ghi đè
Sử dụng override để ghi đè
Không có hàm khởi tạo và hàm hủy
Có hàm khởi tạo và hàm hủy
Không thể khai báo phạm vi truy cập, mặc định là public
Có thể khai báo phạm vi truy cập
Dùng để định nghĩa 1 khuôn mẫu chung
Định nghĩa thành phần chung của lớp, sử dụng chung cho nhiều đối tượng cùng kiểu
Tốn thời gian để tìm phương thức tương ứng với lớp nên xử lý chậm hơn
Nhanh hơn so với interface
Qua bài này, chúng ta nắm được các kiến thức cơ bản để lập trình với C#. Các bạn hãy ôn tập thật kỹ để sang bài sau, chúng ta sẽ áp dụng tất cả các kiến thức này để lập trình một số phần mềm đơn giản trên Windows nhé.

Bài viết liên quan

Interface trong C# Interface trong C#
Tính đa hình trong C# Tính đa hình trong C#
Tính kế thừa trong C# Tính kế thừa trong C#
this và static trong C# this và static trong C#
Các loại phạm vi truy cập trong C# Các loại phạm vi truy cập trong C#
Hàm khởi tạo và hàm hủy Hàm khởi tạo và hàm hủy