Saturday, September 7, 2024
HomeC# Programing LanguageSự khác nhau giữa Fields và Properties trong C#

Sự khác nhau giữa Fields và Properties trong C#

Fields (các trường), và Properties (các thuộc tính) là một trong số các thành phần được sử dụng nhiều trong lập trình C#. Chức năng của chúng đều là dùng để định nghĩa và quản lý dữ liệu trong các đối tượng. Nhưng về cách triển khai và bối cảnh sử dụng thì lại hoàn toàn khác nhau, chúng ta sẽ cùng tìm hiểu sự khác nhau đó trong bài viết này.

1. Fields là gì ?

Fields hay còn gọi là các trường, các biến được khai báo trực tiếp trong các Class hoặc Struct nhằm mục đích lưu trữ giá trị của dữ liệu và chúng thường được khai báo cùng với các Access Modifiers như: publicprivateprotectedinternalprotected internal, hoặc private protected để kiểm soát quyền truy cập đến chúng từ bên ngoài các Class hoặc Struct.
Ví dụ 1.1: Một ví dụ về cách khai báo Fields

C#
 public class Person
 {
     public string? _name;
     private int _age;
 }

Ở ví dụ bên trên, chúng ta khai báo hai Fields là _name có Access Modifiers là public, kiểu dữ liệu string và _age có Access Modifiers là private kiểu dữ liệu int. Hai fields này nằm trực tiếp trong Class có tên là Person

Tiếp theo chúng ta xem cách mà Class gọi đến Fields như thế nào, chúng ta sẽ viết thêm cho ví dụ bên trên một số chức năng
Ví dụ 1.2:

C#
 public class Person
 {
     public string? _name = "Your name";
     private int _age = 18;

     public Person()
     {
         Console.WriteLine($"Name: {_name} - Age: {_age}");
         _name = "Jakate";
         _age = 22;
         Console.WriteLine($"Name: {_name} - Age: {_age}");
         UpdatePerson();
         Console.WriteLine($"Name: {_name} - Age: {_age}");
     }
     public void UpdatePerson()
     {
         _name = "Kane";
         _age = 23;
     }
 }

Ở code ví dụ bên trên, đầu tiên chúng ta vừa khai báo vừa init giá trị mặc định cho hai fields mặc định _name là “Your name” và _age là 18, sau đó ở Constructor chúng ta sẽ thay đổi giá trị thành _name là “Jakate” và _age là 22 sau đó là một Method cập nhật giá trị mới _name là “Kane” và _age là 23. Trong khi thay đổi các giá trị, chúng ta sẽ lồng các dòng lệnh in kết quả ra màn hình để theo dõi các giá trị thay đổi. Sau đó chúng ta sẽ tạo một đối tượng Person trong phương thức Main và chạy chương trình chúng ta sẽ thấy kết quả như sau:

Name: Your name - Age: 18
Name: Jakate - Age: 22
Name: Kane - Age: 23

Như vậy chúng ta có thể thấy trong một đối tượng, khi đối tượng được gọi thì Fields sẽ được gọi đầu tiên ở phần khai báo, sau đó chúng sẽ được thay đổi giá trị trong Constructor nếu có, và cuối cùng là các Method sẽ thay đổi giá trị của chúng nếu có. Đó cũng chính là thứ tự quản lý Fields trông một đối tượng.

Ngoài ra Fields sẽ xuất hiện trong một Properties với vài trò là một backing store hay backing field
Ví dụ 1.3:

C#
 private int myVar;
 public int MyProperty
 {
     get { return myVar; }
     set { myVar = value; }
 }

Ví dụ bên trên là một triển khai của Properties và chúng ta thấy có sự xuất hiện của một Fields có tên là myVar, vai trò của myVar lúc này là một backing store hay backing field. Chúng ta sẽ đi sâu vào tìm hiểu Properties ở phần sau.

1.1 Các loại Fields thường dùng

Chúng ta có thể khai báo Fields với từ khóa readonly, có nghĩa là chúng ta chỉ có thể cập nhật giá trị của Fields tại lúc khai báo hoặc trong Constructor.

Chúng ta có thể khai báo Fields với từ khóa static, có nghĩa là chúng chỉ có một instance duy nhất cho dù tạo nhiều đối tượng đi nữa, và chúng sẽ được gọi trực tiếp thông qua tên Class mà không phải tạo ra một đối tượng mới. Về bản chất static Fields chính là một singleton trong ứng dụng.
Ví dụ 1.4:

C#
    internal class Program
    {
        static void Main(string[] args)
        {
            Rectangle.Width = 100; // Truy cập static Fields
            Console.ReadKey();
        }
    }
    
    public class Rectangle
    {
       public static int Width = 2;
    }

Xem ví dụ trên chúng ta có thể thấy một field có tên Width có giá trị là 2 được truy cập trực tiếp trong hàm Main thông qua tên lớp Rectangle

Chúng ta có thể dùng static readonly và chúng sẽ tương tự như dùng constant, nhưng chúng sẽ quyết định giá trị ở thời điểm runtime thay vì thời điểm compile như khi dùng const
Ví dụ 1.5:

C#
  public class Rectangle
  {
     public static readonly int Width = 2; // Same as constantt fields
     public const int Height = 2; // Constant
  }

Chúng ta có thể sử dụng Fields với từ khóa required nhưng khi tạo đối tượng chúng ta phải đồng thời khởi tạo giá trị cho required fields
Ví dụ 1.6:

C#
 internal class Program
 {
     static void Main(string[] args)
     {
         Rectangle rec1= new(); // Không thể tạo đối tượng
         Rectangle rec2 = new() { Width = 2}; // Chỉ có thể tạo đối tượng nếu khởi tạo giá trị cho Required Fields
         Console.ReadKey();
     }
 }
 
 public class Rectangle
 {
     public required int Width;
 }

Ở ví dụ phía trên đối tượng rec1 sẽ báo lỗi nếu bạn không khởi tạo giá trị cho Width như đối tượng rec2.
Lỗi nhận được: Error CS9035 Required member ‘Rectangle.Width’ must be set in the object initializer or attribute constructor.

Bổ sung, ở phiên bản C# 12 sẽ có khái niệm primary constructor giúp thay thế việc khai báo Fields.

Ưu điểm của Fields: là một cách tiếp cận đơn giản cho việc lưu trữ dữ liệu trong một đối tượng, không đòi hỏi phức tạp về mặt quản lý vì chỉ khống chế thông qua các Access Modifiers là đủ.
Nhược điểm của Fields: sẽ không đảm bảo về mặt an toàn dữ liệu vì cần hạn chế việc public các dữ liệu của đối tượng ra bên ngoài, hoặc nếu truy cập các Fields từ bên ngoài sẽ dễ dàng nếu không quản lý chặt chẽ. Không có các cơ chế để đóng gói, quản lý dữ truy cập và xác minh đầu vào dữ liệu.

2. Properties là gì ?

Properties hay còn gọi là các thuộc tính, là một phương pháp để thực thi tính đóng gói cho các private fields mà chúng ta đã tìm hiểu ở phần trên. Properties cung cấp 2 phương thức là getter và setter để thực hiện các cơ chế điều khiển truy cập đến các backing fields, dùng get để lấy giá trị cho thuộc tính và set để đặt các giá trị cho thuộc tính. Hai phương thức get và set còn được gọi là accessor. Trong thuộc tính các accessor có tham số giá trị được gọi là value, có kiểu dữ liệu giống với Properties
Ví dụ: Chúng ta xem lại ví dụ này, chúng ta thấy đây là một full Properties bao gồm backing field là myVar, thuộc tính chính tên là MyProperty trong get và set có tham số value có kiểu dữ liệu int.
Ví dụ 2.1:

C#
 private int myVar;
 public int MyProperty
 {
     get { return myVar; }
     set { myVar = value; }
 }

Ngoài ra, Properties còn được biểu diễn dưới dạng auto-implement
Ví dụ: xem ví dụ bên dưới ta thấy Properties được biểu diễn ngắn gọn, gọi là biểu diễn auto-implement lúc này Properties sẽ ẩn đi backing fields nhưng sẽ hoàn toàn đồng nghĩa như một full-properties, vì ngầm định nó sẽ tự tạo ra các backing field và thực thi các getter và setter thông qua tham số value kiểu int (ví dụ 2.1 phía trên).
Ví dụ 2.2:

C#
 public int MyProperty { get; set; }

2.1 Các kiểu thuộc tính

Kiểu read-only: thuộc tính kiểu read-only sẽ chỉ có thể lấy giá trị ra đọc bằng phương thức getter mà không thể ghi giá trị khác vào bằng setter.
Ví dụ 2.3: Hãy xem ví dụ bên dưới, ở class Student có thuộc tính Name nhưng chỉ dùng phương thức get, cho nên thuộc tính này chỉ lấy giá trị ra đọc mà không để đặt giá trị mới.

C#
  internal class Program
  {
      static void Main(string[] args)
      {
          Student student = new();
          student.Name = "John Conner"; // Error: không thể đặt giá trị cho thuộc tính
          Console.ReadKey();
      }
  }
  public class Student
  {
      private string _name = "";
      public string Name
      {
          get { return _name; }
      }
  }

Kiểu write-only: ngược lại kiểu write-only thì thuộc tính chỉ có thể thay đổi giá trị mà không để lấy giá trị ra để đọc.
Ví dụ 2.4: ở line số 6 bạn có thể đặt giá trị bằng setter nhưng tại line số 7 bạn muốn đọc giá trị này sẽ báo lỗi.

C#
 internal class Program
 {
     static void Main(string[] args)
     {
         Student student = new();
         student.Name = "John Conner"; // Đặt giá trị cho thuộc tính
         Console.WriteLine($"Show name: {student.Name}"); // Error: không thể lấy ra giá trị
         Console.ReadKey();
     }
 }
 public class Student
 {
     private string _name = "";
     public string Name
     {
         set { _name = value; }
     }
 }

Kiểu init-only: Properties kiểu init-only chỉ có thể thay đổi các giá trị trong lúc khởi tạo đối tượng mà không thể thay đổi ở nơi khác.
Ví dụ 2.5: các thuộc tính Name và Age chỉ được đặt giá trị khi khởi tạo đối tượng Student, nếu ngoài phạm vi này chương trình sẽ báo lỗi.

C#
 internal class Program
 {
     static void Main(string[] args)
     {
         Student student = new() { Name ="Kane J", Age = 18};

         student.Name = "Lih Agr"; // Error
         student.Age = 25; // Error

         Console.ReadKey();
     }
 }
 public class Student
 {
     public string? Name { get; init; }
     public int Age { get; init; }
 }

Kiểu static: giống như Fields, Properties kiểu static sẽ chỉ có một instance tồn tại trong tất cả các đối tượng được tạo từ một class. Điều này sẽ phù hợp trong các trường hợp nếu bạn cần tạo ra một Properties có giá trị không thay đổi trong bất kỳ đối tượng nào được tạo ra, đóng vai trò là một hệ số chung, một đơn vị,…
Ví dụ 2.6: class Student có một thuộc tính là ClassRoom được truy cập thông qua tên lớp Student.ClassRoom.

C#
  internal class Program
  {
      static void Main(string[] args)
      {
          Console.WriteLine($"Student class room: {Student.ClassRoom}");
          Console.ReadKey();
      }
  }
  public class Student
  {
      public static string ClassRoom { get; set; } = "Room 9";
  }

Kiểu Virtual và Abstract:
– Thuộc tính sử dụng từ khóa virtual sẽ cho phép các lớp kế thừa ghi đè giá trị thông qua từ khóa override.
Ví dụ 2.7: lớp Program sẽ kế thừa lớp Student và override lại thuộc tinh ClassRoom và đổi giá trị sang Room 10.

C#
internal class Program : Student
{
    public override string ClassRoom { get; set; } = "Room 10";
   
    static void Main(string[] args)
    {
        Program program = new();
        Console.WriteLine($"Show class room: {program.ClassRoom}");
    }
}
public class Student
{
    public virtual string ClassRoom { get; set; } = "Room 9";
}

– Thuộc tính sử dụng từ khóa abstract phải được thể hiện trong một class abstract và việc thực thi sẽ được biểu diễn trong lớp kế thừa của nó.
Ví dụ 2.8: trong trường hợp sử dụng abstract Properties thì việc implement là bắt buộc trong lớp kế thừa.

C#
 internal class Program : Student
 {
     public override string ClassRoom { get; set; } = "Room 10";

     static void Main(string[] args)
     {
         Program program = new();
         Console.WriteLine($"Show class room: {program.ClassRoom}");
     }
 }
 public abstract class Student
 {
     public abstract string ClassRoom { get; set; }
 }

3. Sự khác nhau giữ Fields và Properties

Qua các khái niệm cùng với ví dụ về Fields và Properties được cung cấp bên trên thì chúng ta có thể dễ dàng nhận ra sự khác biệt giữa chúng. Sự khác biệt này chính là khả năng truy cập, tính đóng gói và mức độ kiểm soát mà chúng cung cấp trong việc truy cập dữ liệu.

Fields cung cấp một các trực tiếp việc truy cập dữ liệu với các Access Modifiers và thiếu đi tính đóng gói và bảo vệ dữ liệu nên có thể làm cho chúng bị rò rỉ dữ liệu do các tác động truy cập từ bên ngoài.

Mặc khác Properties sẽ đóng gói dữ liệu và kiểm soát quyền truy cập thông qua các Accessor getter và setter. Properties cho phép nhiều sự kiểm soát hơn về mặt xuất hiện và tùy chỉnh đối với dữ liệu.

Tính đóng gói,đây là một khái niệm quan trọng trong kỹ thuật lập trình OOP. Fields không đóng gói dữ liệu và phơi bày dữ liệu nếu chúng ta cố ý hoặc vô tình sử dụng public accessor. Mặt khác Properties sẽ kiểm soát việc này tốt hơn.
Ví dụ 3.1: đây là một ví dụ về nhập tuổi, Properties có tên Age sẽ kiểm soát số tuổi đầu vào luôn >=18 và nếu ngược lại thì sẽ mặc định là 18 tuổi, đồng thời đưa ra đoạn cảnh báo “Invalid age!”. Đây là một ví dụ về cách kiểm soát dữ liệu của Properties.

C#
internal class Program 
{

    static void Main(string[] args)
    {
        Program program = new();
        Student student = new();
        Console.WriteLine("Input Age: ");
        var ageInput = Console.ReadLine();
        student.Age = (int.Parse(ageInput));
        Console.WriteLine($"Show Age: {student.Age}");
    }
}
public  class Student
{
    private int _age;
    public int Age
    {
        get { return _age; }
        set 
        { 
            if(value < 18)
            {
                Console.WriteLine("Invalid age!");
                _age = 18;
            }
            else
            {
                _age = value;
            }
        }
    }
}

4. Khi nào dùng Fields và khi nào dùng Properties

Từ các ví dụ và các giải thích chi tiết về các đặt điểm của Fields và Properties thì chúng đều là những thành phần quan trọng trong việc quản lý dữ liệu và đều có ưu và nhược điểm riêng không thể thiếu đi một trong hai nếu làm việc với một chương trình C# về OOP.

Sử dụng Fields nếu bạn cần một loại thành phần quản lý dữ liệu đơn giản, có thể trực tiếp thay đổi các giá trị nội bộ trong đối tượng và không cần phải xác minh dữ liệu hoặc không cần phải suy nghĩ về chuyện khống chế truy cập từ bên ngoài đối tượng, chỉ dùng nội bộ thì Fields là một cách tiếp cận hữu hiệu và nhanh chóng.

Sử dụng Properties nếu chúng ta muốn quản lý đầu vào, xác minh đầu vào dữ liệu. Ngoài ra, nếu bạn muốn truy cập các dữ liệu từ bên ngoài bản nên sử dụng Properties vì bạn sẽ toàn quyền khống chế xem liệu rằng dữ liệu của bạn có thể chỉ đọc hoặc chỉ có thể ghi hoặc cả đọc và ghi trong các trường hợp cụ thể mà bạn quy định. Properties còn khống chế việc truy cập từ bên ngoài theo điều kiện mà người thiết kế sẽ quy định, điều này đảm bảo tính đóng gói của dữ liệu, giúp cho việc quản lý dữ liệu một cách chuyên nghiệp và hạn chế tối đa các lỗi rò rỉ dữ liệu ra bên ngoài. Fields còn đóng vai trò là một backing store cho Properties vì vậy chúng sẽ có mối liên hệ cộng tác với nhau để quản lý dữ liệu trên đối tượng hiệu quả.

Nguyễn Minh Châu
Nguyễn Minh Châuhttps://laptrinhdotnet.com
Hi guys ! I'm a software developer. I love programming and new technologies. I create non-professional content on this website, you can only view it for reference purposes.
RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here