Tính đóng gói trong C#

Tính đóng gói (Encapsulation) là một trong 4 tính chất trụ cột của lập trình hướng đối tượng OOP.

  • Tính đóng gói (Encapsulation).
  • Tính kế thừa (Inheritance).
  • Tính đa hình (Polymorphism).
  • Tính trừu tượng (Abstraction).

Nếu chưa hiểu rõ về lập trình hướng đối tượng thì bạn nên tham khảo bài viết dưới đây trước:

Lập trình hướng đối tượng (OOP) trong C# | Comdy
Lập trình hướng đối tượng (OOP) trong C# là gì? 5 khái niệm và 4 tính chất quan trọng của lập trình hướng đối tượng là gì?

Tính đóng gói là khả năng che giấu thông tin của đối tượng với môi trường bên ngoài. Việc cho phép môi trường bên ngoài tác động lên các dữ liệu nội tại của đối tượng hoàn toàn tùy thuộc vào người viết mã.

Tính đóng gói được thực hiện bằng cách sử dụng các chỉ thị truy cập. Một chỉ thị truy cập xác định phạm vi và khả năng truy cập của các thành viên lớp (trường, thuộc tính, phương thức). C# hỗ trợ các chỉ thị truy cập sau:

  • public
  • private
  • protected
  • internal
  • protected internal

Chỉ thị truy cập public

Chỉ thị truy cập public cho phép một lớp đưa ra các thuộc tính và các phương thức thành viên của nó cho các phương thức và đối tượng khác. Bất kỳ thành viên nào cũng có thể được truy cập từ bên ngoài lớp.

Ví dụ sau minh họa điều này:

using System;

namespace RectangleApplication 
{
   class Rectangle 
   {
      public double Length { get; set; }
      public double Width { get; set; }
      
      public double GetArea() 
      {
         return Length * Width;
      }
      public void Display() 
      {
         Console.WriteLine("Length: {0}", Length);
         Console.WriteLine("Width: {0}", Width);
         Console.WriteLine("Area: {0}", GetArea());
      }
   }
   
   class ExecuteRectangle 
   {
      static void Main(string[] args) 
      {
         Rectangle rectangle = new Rectangle();
         rectangle.Length = 4.5;
         rectangle.Width = 3.5;
         rectangle.Display();
         Console.ReadLine();
      }
   }
}

Kết quả khi chạy chương trình:

Length: 4.5
Width: 3.5
Area: 15.75

Trong ví dụ trên, Width và Length là thuộc tính được khai báo public, do đó chúng có thể được truy cập từ phương thức Main() bằng cách tạo một thể hiện của lớp Rectangle, có tên rectangle .

Phương thức Display()GetArea() cũng có thể truy cập trực tiếp các thuộc tính này do đều là thành viên của lớp Rectangle.

Các phương thức Display () cũng được khai báo public, do đó nó cũng có thể được truy cập từ phương thức Main() bằng cách sử dụng thể hiện rectangle của lớp Rectangle.

Chỉ thị truy cập private

Chỉ thị truy cập private cho phép lớp ẩn các trường và các phương thức thành viên khỏi các phương thức và đối tượng khác.

Chỉ các thành viên trong cùng một lớp mới có thể truy cập các thành viên private của nó. Ngay cả thể hiện của lớp cũng không thể truy cập các thành viên private của nó.

Nếu một trường hoặc phương thức không có chỉ thị truy cập thì nó sẽ được gắn chỉ thị truy cập mặc định là private.

Ví dụ sau minh họa điều này:

using System;

namespace RectangleApplication 
{
   class Rectangle 
   {
      private double _length;
      private double _width;
      
      public Rectangle(double length, double width)
      {
      	  _length = length;
          _width = width;
      }
      
      public double GetArea() 
      {
         return _length * _width;
      }
      
      public void Display() 
      {
         Console.WriteLine("Length: {0}", _length);
         Console.WriteLine("Width: {0}", _width);
         Console.WriteLine("Area: {0}", GetArea());
      }
   }
   
   class ExecuteRectangle 
   {
      static void Main(string[] args) 
      {
         Rectangle rectangle = new Rectangle(4.4, 3.3);
         rectangle.Display();
         Console.ReadLine();
      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

Length: 4.4
Width: 3.3
Area: 14.52

Trong ví dụ trên, _width và _length của trường thành viên của lớp được khai báo là private, do đó chúng không thể được truy cập từ phương thức Main().

Các phương thức thành viên GetArea()Display() có thể truy cập các trường này do chúng đều là thành viên của lớp Rectangle.

Do các phương thức thành viên GetArea()Display() được khai báo public nên chúng có thể được truy cập từ phương thức Main() bằng cách sử dụng thể hiện rectangle của lớp Rectangle.

Chỉ thị truy cập protected

Chỉ thị truy cập protected cho phép lớp ẩn các trường và các phương thức thành viên khỏi các phương thức và đối tượng khác. Nó chỉ cho phép một lớp con truy cập các trường và các phương thức thành viên này của lớp cha (hoặc lớp cơ sở).

Chỉ các thành viên trong class cha và class con mới có thể truy cập các thành viên protected của lớp cha. Ngay cả thể hiện của lớp con hay lớp cha cũng không thể truy cập các thành viên protected của class cha.

Chỉ thị truy cập protected tương tự chỉ thị truy cập private, chỉ khác là nó cho phép class con truy cập các trường và phương thức thành viên của class cha có chỉ thị protected.

Chương trình sau đây minh họa điều này:

using System;

namespace InheritanceApplication 
{
   class Shape 
   {
      protected int _width;
      protected int _height;
      
      public void SetWidth(int width) 
      {
         _width = width;
      }
      public void SetHeight(int height) 
      {
         _height = height;
      }
      public void Display()
      {
         Console.WriteLine("Width = {0}, Height = {1}", _width, _height);
      }
   }

   // Derived class
   class Rectangle: Shape 
   {
      public int GetArea() 
      { 
         return (_width * _height); 
      }
   }
   
   class RectangleTester 
   {
      static void Main(string[] args) 
      {
         Shape shape = new Shape();
         shape.SetWidth(4);
         shape.SetHeight(6);
         shape.Display();
      
         Rectangle rectangle = new Rectangle();

         rectangle.SetWidth(5);
         rectangle.SetHeight(7);
         rectangle.Display();

         Console.WriteLine("Total area: {0}",  rectangle.GetArea());
         Console.ReadKey();
      }
   }
}

Kết quả khi biên dịch và chạy chương trình:

Width = 4, Height = 6
Width = 5, Height = 7
Total area: 35

Trong ví dụ trên, _width và _height của trường thành viên của lớp Shape được khai báo là protected, do đó chúng không thể được truy cập từ phương thức Main().

Lớp Rectangle kế thừa lớp Shape nên có thể truy cập được trường _width và _height để tính diện tích trong phương thức GetArea().

Các phương thức thành viên SetWidth(), SetHeight()Display() của class Shape là public nên chúng được class Rectangle kế thừa và có thể truy cập ở phương thức Main().

Phương thức thành viên GetArea() được khai báo public nên chúng có thể được truy cập từ phương thức Main() bằng cách sử dụng thể hiện rectangle của lớp Rectangle.

Chỉ thị truy cập internal

Chỉ thị truy cập internal cho phép một lớp đưa ra các trường và phương thức thành viên của nó cho các phương thức và đối tượng khác trong cùng assembly.

Nói cách khác, bất kỳ thành viên nào có chỉ thị truy cập internal đều có thể được truy cập từ bất kỳ lớp hoặc phương thức nào được định nghĩa trong cùng một assembly.

Chỉ thị truy cập internal tương tự như chỉ thị public, nó chỉ khác ở chỗ chỉ thị internal giới hạn phạm vi truy cập trong cùng một assembly còn chỉ thị public thì không có bất kỳ giới hạn nào.

Nếu một class không có chỉ thị truy cập thì chỉ thị truy cập mặc định sẽ là internal.

Chương trình sau đây minh họa điều này:

using System;

namespace RectangleApplication 
{
   public class Rectangle 
   {
      internal double Length;
      internal double Width;
      
      // private method
      double GetArea() 
      {
         return Length * Width;
      }
      
      public void Display() 
      {
         Console.WriteLine("Length: {0}", Length);
         Console.WriteLine("Width: {0}", Width);
         Console.WriteLine("Area: {0}", GetArea());
      }
   }
   
   class ExecuteRectangle 
   {
      static void Main(string[] args) 
      {
         Rectangle rectangle = new Rectangle();
         rectangle.Length = 4.5;
         rectangle.Width = 3.5;
         rectangle.Display();
         Console.ReadLine();
      }
   }
}

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau:

Length: 4.5
Width: 3.5
Area: 15.75

Trong ví dụ trên, lưu ý rằng phương thức GetArea() không được khai báo với bất kỳ chỉ định truy cập nào nên sẽ chỉ thị truy cập mặc định là private.

Hai trường WidthLength có thể được truy cập từ phương thức Main() thông qua thể hiện rectangle của lớp Rectangle.

Tuy nhiên nếu tạo một project mới và tạo một thể hiện của lớp Rectangle ở trong project mới này thì sẽ không thể truy cập được trường Width Length.

Chỉ thị truy cập protected internal

Chỉ thị truy cập protected internal cho phép lớp ẩn các trường và các phương thức thành viên khỏi các phương thức và đối tượng khác. Nó chỉ cho phép một lớp con cùng assembly với lớp cha truy cập các trường và các phương thức thành viên này của lớp cha (hoặc lớp cơ sở).

Do có chỉ thị truy cập internal nên các trường và phương thức thành viên của lớp cha có thể truy cập thông qua thể hiện của lớp cha hoặc lớp con.

Ví dụ sau đây sẽ minh họa những điều trên:

using System;

namespace InheritanceApplication 
{
   class Shape 
   {
      protected internal int Width;
      protected internal int Height;
      
      public void Display()
      {
         Console.WriteLine("Width = {0}, Height = {1}", Width, Height);
      }
   }

   // Derived class
   class Rectangle: Shape 
   {
      public int GetArea() 
      { 
         return (Width * Height); 
      }
   }
   
   class RectangleTester 
   {
      static void Main(string[] args) 
      {
         Shape shape = new Shape();
         shape.Width = 4;
         shape.Height = 6;
         shape.Display();
      
         Rectangle rectangle = new Rectangle();

         rectangle.Width = 5;
         rectangle.Height = 7;
         rectangle.Display();

         Console.WriteLine("Total area: {0}",  rectangle.GetArea());
         Console.ReadKey();
      }
   }
}

Kết quả khi biên dịch và chạy chương trình:

Width = 4, Height = 6
Width = 5, Height = 7
Total area: 35
Lập Trình C#Lập Trình C# Cơ Bản
Bài Viết Liên Quan:
int[] và int[,] trong C#: Ai nhanh hơn
Trung Nguyen 10/10/2020
int[] và int[,] trong C#: Ai nhanh hơn

Hiểu được sự khác biệt giữa các loại mảng trong C# sẽ giúp bạn chọn cấu trúc dữ liệu chính xác cho mọi trường hợp.

Struct và class trong C#: Ai nhanh hơn
Trung Nguyen 09/10/2020
Struct và class trong C#: Ai nhanh hơn

Trong bài viết này, tôi sẽ so sánh sự khác biệt về hiệu suất giữa struct và class trong C#: Ai nhanh hơn.

Best practice cho performance trong C#
Trung Nguyen 03/10/2020
Best practice cho performance trong C#

Mục tiêu của bài viết này là cung cấp một danh sách không đầy đủ các code mẫu cần tránh, vì chúng rủi ro hoặc performance kém.

Đọc ghi file (File I/O) trong C#
Trung Nguyen 26/04/2020
Đọc ghi file (File I/O) trong C#

Hướng dẫn này sẽ giúp bạn tìm hiểu về đọc ghi file (File I/O) trong C# và sử dụng các lớp tiện ích để đọc ghi file.