This article is currently in the process of being translated into Vietnamese (~56% done).
Properties
Trong bài trước chúng ta đã nói về trường. Chúng giống biến toàn cục trong class, cho phép bạn truy cập vào chúng từ tất cả các phương thức. Chúng ta cũng bàn về trường CÓ THỂ truy cập từ các lớp khác nếu chúng được khai báo là public, nhưng không nên dùng. Với biến/ trường mà bạn muốn truy cập từ bên ngoài lớp thì bạn nên dùng thuộc tính.
Khi bạn khai báo một trường là public thì bạn cho phép được truy cập nó từ bên ngoài - các lớp khác có thể làm bất kỳ cái gì nếu họ muốn, không cần để ý tới việc khai báo lớp. Thuộc tính cho phép kiểm soát lớp, bằng cách khai báo một trường là đọc, chỉ ghi và thậm chí cho phép khai báo lớp để kiểm tra và thao tác với gái trị trước khi trả về hay gán cho trường.
Một thuộc tính giống như giao thoa giữa trường và phương thức, vì nó được khai báo giống trường với phạm vi, kiểu dữ liệu và tên nhưng nó cũng có thân giống hàm để kiểm soát thao tác:
public string Name
{
get { return _name; }
set { _name = value; }
}
Để ý các từ khóa get và set. Chúng được dùng cho thuộc tính để kiểm soát việc đọc (get) và viết (set) trường. Bạn có thể có các thuộc tính với chỉ get HOẶC set, để tạo ra thuộc tính chỉ đọc hay chỉ viết, bạn cũng có thể kiểm soát phạm vi của get hay set, ví dụ để tạo ra thuộc tính có thể đọc từ mọi nơi (public) nhưng chỉ thay đổi trong lớp (private).
Bạn cũng chú ý rằng tôi có đề cập đến trường _name. Bạn sẽ phải khai báo trong lớp để thuộc tính có thể dùng nó. Một mẫu thông dụng cho trường và thuộc tính sẽ như sau:
private string _name = "John Doe";
public string Name
{
get { return _name; }
set { _name = value; }
}
Bây giờ bạn có thể thấy trường và thuộc tính cùng làm việc: phương thức get sẽ trả về giá trị của trường_name, trong khi phương thức put sẽ gán giá trị vào trường _name. Trong phương thức set, chúng ta dùng từ khóa đặc biệt value chỉ định giá trị được đưa vào thuộc tính.
Tại điểm này mọi thứ rất cơ bản, nhưng sau đây bạn có thể kiểm soát nhiều hơn cách mà class có thể làm việc với trường và bạn có thể thực hiện thông qua thuộc tính, bạn được tự do để thay đổi cách thực hiện mà không ảnh hưởng gì tới những ai dùng class của bạn. Vid sụ, thuộc tính Name có thể thay đổi như sau:
private string _name = "John Doe";
public string Name
{
get
{
return _name.ToUpper();
}
set
{
if(!value.Contains(" "))
throw new Exception("Please specify both first and last name!");
_name = value;
}
}
Phương thức get bây giờ sẽ trả về giá trị luôn là VIẾT HOA, không quan trọng bên trong trường là dạng chữ nào. Trong phương thức set, chúng ta thêm vào một vài dòng code để kiểm tra liệu chúng ta có truyền giá trị chứa khoảng trắng hay không, bởi chúng ta quyết định rằng tên luôn chứa cả tên và họ - nếu không thì có ngoại lệ. Việc này rất đơn giản, nhưng nó cho chúng ta hình dung về cách kiểm soát khi dùng thuộc tính.
Read-only properties
Most properties you'll see in the examples of this tutorial will be both readable and writeable, because that's the most common usage of properties, but it doesn't always have to be like that. First of all, you can declare a property with only a get-method, like this:
private string _name = "John Doe";
public string Name
{
get { return _name; }
}
In this case, you can no longer change the "Name" property - you can only read it and the compiler will throw an error if you try to assign a value to it. You can still change the value of it from inside the class though, since you can simply assign a new value to the backing field "_name". Doing it that way kind of negates one of the biggest advantage to properties though: The ability to always control whether a value can be accepted. As we already talked about, the set-method is a great way to perform validation of the value, but if you assign a new value to the _name field from multiple places, because the property is read-only, you don't get this validation.
Fortunately for us, C# offers a solution to this: You can define a set method on the property, but limit its visibility, using e.g. the private or the protected keyword. This will give you the best of both worlds, where you can still assign a value to the property from inside the class (or any inherited class if you use the protected keyword) and have it validated accordingly. Here's an example:
private string _name = "John Doe";
public string Name
{
get { return _name; }
private set
{
if(IsValidName(value))
this._name = value;
}
}
public bool IsValidName(string name)
{
return name.EndsWith("Doe");
}
The key difference here is simply the "private" keyword right in front of the "set" keyword, and as mentioned, you can replace it with e.g. protected or internal, depending on your needs.
Auto-implemented properties
Trong một vài trường hợp, bạn không cần kiểm soát trường và nó có thể dài dòng để thao tác cả trường và thuộc tính với phương thức get và set, không thực hiện gì ở ví dụ đầu tiên. Bạn có thể thử khai báo biến như là public để tránh toàn bộ rắc rối này. Nhưng không làm vậy! Rất may cho chúng ta, Microsoft quyết định đưa vào thuộc tính tự động thực hiện trong C# phiên bản 3, cho phép chúng ta tiết kiệm nhiều dòng code. Xem sự khác biệt sau:
Regular property with a declared backing field:
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
The exact same behavior, but with an auto-implemented property:
public string Name { get; set; }
Chú ý các phương thức get và set là rỗng và không có trường được khai báo bên trong - hay nói cách khác, chúng ta có thể thực hiện y hệt ví dụ đầu tiên nhưng chỉ trong một dòng code! Nhớ rằng trường bên trong vẫn tồn tại ở thời gian chạy - nó sẽ được tự động thực hiện bởi trình biên dịch, như tên chỉ ra. Bạn có thể quyết đinh rằng bạn cần nhiều điều khiển hơn ở thuộc tính nào đó thì bạn chỉ đơn giản đổi nó sang trường/thuộc tính thông thường và thự hiện các phương thức get và set.
Notice that you are still left with an important mechanism of control from regular properties when using auto-implemented properties: You can leave out the set keyword to create a read-only property, e.g. like this:
public string ReadOnlyProperty { get; }
Write-only properties are not allowed when using auto-implemented properties.
Auto-implemented properties with default values
Prior to C# version 6, you could not define a default value for an auto-implemented property - for that, you would need a declared backing field, which would allow you to initialize the variable with a value:
private string _name = "John Doe";
public string Name
{
get { return _name; }
set { _name = value; }
}
But in C# version 6, Microsoft finally added the ability to initialize an auto-implemented property with a default value, like this:
public string Name { get; set; } = "John Doe";
Expression-bodied properties
Another property-related feature Microsoft implemented in C# 6.0 and 7.0 is the expression bodied members. It simply allows you to write single-line expressions for your properties and methods - in this case, let's look at how to use it for your get/set methods in a way that takes up less space and requires slightly less typing:
private string name;
public string Name
{
get => name;
set => name = value;
}
If your property is read-only, the syntax can be even shorter:
public string Name => "John Doe";
Of course this also works if you need to actually do something before returning the value, like this:
public string Name { get; set; } = "John Doe";
public string FirstName => this.Name.Substring(0, this.Name.IndexOf(" "));
As you can see, this allows you to define a get method but without the actual get and return keywords, while encouraging you to keep it all in one line instead of multiple lines.
Summary
Properties gives your classes more control over how fields can be accessed and manipulated, and they should always be used when you want to give access to fields from outside of the declaring class.