– Nội dung của bài viết này mình sẽ trình bày về interface trong C#. Đây là khái niệm rất cơ bản trong lập trình hướng đối tượng nói chung và lập trình C# nói riêng.
– Nếu bạn nào đã tìm hiểu và làm việc với ngôn ngữ lập trình Java thì khái niệm interface hoàn toàn không xa lạ. Vậy interface là gì? Mục đích sử dụng của nó? Chúng ta sẽ cùng tìm hiểu trong bài viết này.
1. Dẫn nhập
Ví dụ 1: Để dễ hình dung các bạn có thể liên tưởng đến con ếch, nó là loài lưỡng cư:
- Vừa là động vật trên cạn.
- Vừa là động vật có thể sống dưới nước.
Nên nó có thể “kế thừa” 2 lớp động vật này, nhưng trong C# chỉ hỗ trợ đơn thừa kế, mà không hỗ trợ đa thừa kế.
Ví dụ 2: Cách đánh giá kết quả của sinh viên tại các trường đại học và các trung tâm như trung tâm tiếng Anh, trung tâm Tin học,… là khác nhau, chẳng hạn như đối với loại Giỏi thì:
- Đối với trường đại học thì tổng điểm >= 3.6
- Còn ở trung tâm tiếng Anh thì tổng điểm >= 85%
Rõ ràng ở đây, chúng ta thấy cùng một quy tắc xếp loại Giỏi nhưng cách tính và kết quả đưa ra là khác nhau.
Thông qua 2 ví dụ này thì:- Làm thế nào để một lớp có thể sử dụng chung được các thuộc tính và phương thức của 2 hay nhiều lớp khác?
- Làm thế nào để áp dụng các quy tắc khác nhau, tùy vào hoàn cảnh khác nhau của từng đối tượng?
=> Tất cả những điều này sẽ được giải quyết bằng cách sử dụng interface.
2. Interface là gì? Và cú pháp khai báo
#1. Interface là gì?
– Interface được hiểu là lớp nền (lớp base), tức là chỉ có nền móng thôi, tất cả mọi thứ còn lại là do chúng ta (các lập trình viên) tự xây dựng trên nền móng đó.
– Interface như là một lớp mặt nạ, như là một bản thiết kế cho các class cùng cách thức hoạt động, nhưng có thể cùng hoặc khác nhau về bản chất bên trong.
#2. Cú pháp khai báo interface
{
// interface member
}
[/code]
Trong đó:
- <access_modifier> : là public hoặc internal, nếu không ghi rõ mặc định là internal.
- interface : từ khóa khai báo một interface.
- <tên_interface> : thường bắt đầu bằng chữ I, ví dụ: IShape, IAnimal, IStudent,…
- <: tên_base_interface> : trường hợp có implement từ interface khác thì dấu 2 chấm : biểu thị sự implement, tiếp theo sau là tên base interface.
■ Ví dụ:
interface IPeople
{
// interface member
}
// Khai báo interface IStudent, implement từ interface IPeople
public interface IStudent : IPeople
{
// interface member
}
[/code]
3. Các đặc điểm của interface
Một interface trông có vẻ giống như một class, nhưng nó chỉ mô tả prototype của các thành phần methods, properties, indexers và events mà không định nghĩa chúng. Các thành phần không được sử dụng trong interface là: constructor, destructor, field, hằng, thành phần static.
Không thể khai báo hay chỉ định phạm vi truy cập (access modifiers) 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.
■ Khai báo một interface IDung, bao gồm các thành phần ĐÚNG:
{
// method
void xyz();
// property
string name { get; set; }
// indexer
double this[int index] { get; set; }
// event
event EventHandler OnChanged;
}
[/code]
■ Khai báo một interface ISai, bao gồm các thành phần SAI:
{
// Không được sử dụng hàm [constructor] // error CS0526: Interfaces cannot contain constructors
ISai() { }
// Không được sử dụng hàm [destructor]
// error CS0575: Only class types can contain destructors
~ISai() { }
// Không được chỉ định phạm vi truy cập (access modifiers)
// error CS0106: The modifier ‘public’ is not valid for this item
public void xyz();
// error CS0106: The modifier ‘protected’ is not valid for this item
protected void abc();
// Không được khai báo [field]
// error CS0525: Interfaces cannot contain fields
int number;
// Không được khai báo hằng [const]
// error CS0525: Interfaces cannot contain fields
public const double PI = 3.14;
// Không được định nghĩa hàm
// error CS0531: ‘ISai.xyz()’: interface members cannot have a definition
void xyz()
{
Console.WriteLine( "Print xyz" );
}
// Không được sử dụng thành phần [static]
// error CS0106: The modifier ‘static’ is not valid for this item
static void xyz();
}
[/code]
Một interface sẽ được implement bởi các classes và structs, khi đó classes và structs bắt buộc phải định nghĩa tất cả các thành phần được mô tả trong interface.
■ Giả sử chúng ta có class Demo được implement từ interface IDung, thì bắt buộc class Demo phải định nghĩa tất cả các thành phần được khai báo trong interface IDung, kết quả như sau:
{
// implement method
public void xyz()
{
throw new NotImplementedException();
}
// implement property
public string name
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
// implement indexer
public double this[int index]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
// implement event
public event EventHandler OnChanged;
}
[/code]
Mỗi class hoặc struct có thể implement một hoặc nhiều interface, trường hợp nhiều interface thì sẽ được ngăn cách nhau bằng dấu phẩy.
class ConEch : IDongVatTrenCan, IDongVatDuoiNuoc
{
// implement method interface IDongVatTrenCan
public void Jump()
{
throw new NotImplementedException();
}
// implement method interface IDongVatDuoiNuoc
public void Swim()
{
throw new NotImplementedException();
}
}
interface IDongVatTrenCan
{
void Jump();
}
interface IDongVatDuoiNuoc
{
void Swim();
}
[/code]
Mỗi interface không được phép kế thừa từ một class nào cả. Chỉ được phép implement từ một hoặc nhiều interface, trường hợp nhiều interface thì sẽ được ngăn cách nhau bằng dấu phẩy. Và khi implement từ base interface thì không thực hiện (không định nghĩa) các thành phần của base interface.
Khác với class. Không thể tạo ra một đối tượng từ interface.
Nếu một lớp implement từ nhiều interface có cùng tên thành viên thì trong lớp phải chỉ rõ thành viên đó thuộc interface nào (explicit interface). Xem demo ở Ví dụ 3 của mục 4.
4. Demo code minh họa cách sử dụng
Ví dụ 1: Demo implement đơn interface. Chúng ta sẽ tạo ra một project MinhHoangBlog để quy định cách xếp loại cho học viên gồm các file sau:
- File IRank.cs : interface IRank đặt ra khuôn mẫu về cách xếp loại cho học viên.
- File Apolo.cs : class implement interface IRank để định nghĩa quy tắc xếp loại cho trung tâm tiếng Anh.
- File University.cs : class implement interface IRank để định nghĩa quy tắc xếp loại cho trường đại học.
- File Program.cs : chứa hàm main() thực hiện chương trình.
■ Nội dung file IRank.cs
{
interface IRank
{
// Khai báo prototype hàm xếp loại
string Ranking(float score);
}
}
[/code]
■ Nội dung file Apolo.cs
{
// Implement interface IRank
class Apolo : IRank
{
// Định nghĩa hàm Ranking
// theo quy tắc xếp loại của trung tâm
public string Ranking(float score)
{
if (score >= 85)
{
return "Distinction";
}
if (score >= 65)
{
return "Credit";
}
if (score >= 40)
{
return "Pass";
}
return "Fail";
}
}
}
[/code]
■ Nội dung file University.cs
{
// Implement interface IRank
class University : IRank
{
// Định nghĩa hàm Ranking
// theo quy tắc xếp loại của trường đại học
public string Ranking(float score)
{
if (score >= 3.6)
{
return "Xuat sac";
}
if (score >= 3.2)
{
return "Gioi";
}
if (score >= 2.5)
{
return "Kha";
}
if (score >= 2.0)
{
return "Trung Binh";
}
return "Khong xep loai";
}
}
}
[/code]
■ Nội dung file Program.cs
namespace MinhHoangBlog
{
class Program
{
static void Main(string[] args)
{
// Khai báo đối tượng của class
Apolo apolo = new Apolo();
University university = new University();
// Gọi hàm xếp loại tương ứng với từng đối tượng
Console.WriteLine("Apolo: " + apolo.Ranking(40));
Console.WriteLine("University: " + university.Ranking(2.6f));
Console.ReadKey();
}
}
}
[/code]
Ví dụ 2: Demo implement đa interface. Chúng ta sẽ tạo ra một project MinhHoangBlog để mô tả tính lưỡng cư của con ếch gồm các file sau:
- File ILandAnimal : interface ILandAnimal đặt ra khuôn mẫu về cách di chuyển của động vật trên cạn.
- File IWaterAnimal.cs : interface IWaterAnimal đặt ra khuôn mẫu về cách di chuyển của động vật dưới nước.
- File Frog.cs : class implement 2 interface ILandAnimal và IWaterAnimal để định nghĩa cách di chuyển ứng với khi trên mắt đất và lúc ở dưới nước.
- File Program.cs : chứa hàm main() thực hiện chương trình.
■ Nội dung file ILandAnimal.cs
{
interface ILandAnimal
{
// Ếch: Di chuyển trên mặt đất là Nhảy
void Jump();
}
}
[/code]
■ Nội dung file IWaterAnimal.cs
{
interface IWaterAnimal
{
// Ếch: Di chuyển dưới nước là Bơi
void Swim();
}
}
[/code]
■ Nội dung file Frog.cs
namespace MinhHoangBlog
{
// Implement 2 interface
class Frog : ILandAnimal, IWaterAnimal
{
// Định nghĩa method interface ILandAnimal
public void Jump()
{
Console.WriteLine( "Frog jumps" );
}
// Định nghĩa method interface IWaterAnimal
public void Swim()
{
Console.WriteLine( "Frog swims" );
}
}
}
[/code]
■ Nội dung file Program.cs
namespace MinhHoangBlog
{
class Program
{
static void Main(string[] args)
{
// Khởi tạo đối tượng lớp con Ếch
Frog frog = new Frog();
// 2 cách di chuyển trên bờ và dưới nước
frog.Jump();
frog.Swim();
Console.ReadKey();
}
}
}
[/code]
Ví dụ 3: Demo implement đa interface và trong các interface có method trùng tên. Chúng ta sẽ tạo ra một project MinhHoangBlog để mô tả tính lưỡng cư của con ếch gồm các file sau:
- File ILandAnimal : interface ILandAnimal khai báo prototype phương thức di chuyển Move().
- File IWaterAnimal.cs : interface IWaterAnimal khai báo prototype phương thức di chuyển Move().
- File Frog.cs : class implement 2 interface ILandAnimal và IWaterAnimal để định nghĩa cách di chuyển ứng với khi trên mắt đất và lúc ở dưới nước.
- File Program.cs : chứa hàm main() thực hiện chương trình.
■ Nội dung file ILandAnimal.cs
{
interface ILandAnimal
{
void Move();
}
}
[/code]
■ Nội dung file IWaterAnimal.cs
{
interface IWaterAnimal
{
void Move();
}
}
[/code]
■ Nội dung file Frog.cs
– Lúc này, vì 2 interface ILandAnimal và IWaterAnimal có cùng tên phương thức là Move(), nên khi định nghĩa, chúng ta cần:
- Chỉ rõ phương thức Move() là của interface nào.
- Bỏ phạm vi truy cập là public đi.
public void Jump() → void ILandAnimal.Move() public void Swim() → void IWaterAnimal.Move()
namespace MinhHoangBlog
{
// Implement 2 interface
class Frog : ILandAnimal, IWaterAnimal
{
void ILandAnimal.Move()
{
Console.WriteLine( "Frog jumps" );
}
void IWaterAnimal.Move()
{
Console.WriteLine( "Frog swims" );
}
}
}
[/code]
■ Nội dung file Program.cs
– Vì 2 interface ILandAnimal và IWaterAnimal có cùng tên phương thức là Move(), nên khi gọi, chúng ta cần: chỉ rõ phương thức Move() là của interface nào, bằng cách dùng từ khóa as để cast về interface tương ứng.
namespace MinhHoangBlog
{
class Program
{
static void Main(string[] args)
{
// Khởi tạo đối tượng lớp con Ếch
Frog frog = new Frog();
// Dùng từ khóa「as」để cast về interface tương ứng khi gọi hàm
(frog as ILandAnimal).Move();
(frog as IWaterAnimal).Move();
Console.ReadKey();
}
}
}
[/code]
[…] Alt + Shift + F10 + Enter: Hiện thực các phương thức kế thừa từ Abstract class hay Interface. […]
[…] – Chỉ cho phép đơn kế thừa, tức là một lớp chỉ được phép kế thừa từ một lớp khác. (vậy làm thế nào một lớp có thể sử dụng được các thuộc tính và phương thức của nhiều lớp khác nhau? Sẽ được trình bày trong bài Interface trong lập trình C#). […]
[…] sự giống và khác nhau giữa hai khái niệm này và biết được khi nào nên dùng interface, khi nào nên dùng abstract class […]
[…] Thuộc tính – Property là một thành viên (member) của một class, struct hay interface. Nó là mở rộng của một trường (field). Property cho phép bạn truy cập vào một […]
[…] Các kiểu tham chiếu còn lại như Class, Interface, Delegate, Dynamic,… chúng ta sẽ nói chi tiết từng loại trong các bài viết […]