Thuộc tính (property), tương tự như các fields trong class. Nhưng với các fields, việc truy cập và sử dụng dữ liệu chỉ đơn giản là đọc hoặc ghi trực tiếp dữ liệu vào mà không có sự kiểm soát. Thông qua property, các dữ liệu trong class được cung cấp quyền kiểm soát chặt chẽ hơn.
Lấy ví dụ, một thời khóa biểu được xem như một class. Nếu không có property, mọi học sinh đều có thể xem (read) và sửa đổi (write) các dữ liệu trên thời khóa biểu đó. Thực tế, HS chỉ có quyền xem mà không được phép sửa đổi. Property sẽ làm nhiệm vụ này.
Đoạn code sau đây triển khai một class TMan, gồm có hai trường private FName và FAge. Dữ liệu trong hai trường này được thể hiện qua hai thuộc tính Name và Age trong published.
type
TMan = class
private
FName: string;
FAge: byte;
published
property Name: string read FName write FName;
property Age: byte read FAge write FAge;
end;
Như trên, nếu không có các thuộc tính Name và Age, các đối tượng bên ngoài class không thể thực hiện xử lí thông tin FName và FAge (vì được private bảo vệ). Thông qua các property trong phần publish, các đối tượng bên ngoài có thể xem và chỉnh sửa được thông tin được ẩn trong class.
Các property thường được public (public hoặc published).
Tiếp theo, hãy đặt vấn đề, thuộc tính Name của một người không được phép sửa đổi (không ai có quyền thay đổi tên của một người khác). Ta tiến hành chỉnh sửa lại khai báo thuộc tính Name.
property Name: string read FName;
Bỏ đi phần write FName, thuộc tính trở thành chỉ đọc (read only), không cho phép thay đổi. Nếu bỏ đi phần read FName, thuộc tính chỉ cho phép chỉnh sửa (write only) mà không cho phép đọc.
Lấy ví dụ người đàn ông ở trên (class TMan). Liệu ông ta có muốn chia sẻ tên tuổi của bản thân cho người không quen biết hay không. Chắc chắn là không.
type
TMan = class
IsNguoiQuen: boolean;
private
FName: string;
FAge: byte;
function GetName: string;
function GetAge: byte;
published
property Name: string read GetName;
property Age: byte read GetAge;
end;
function TMan.GetName: string;
begin
if IsNguoiQuen = true then
Result := FName;
end;
function TMan.GetAge: byte;
begin
if IsNguoiQuen = true then
Result := FAge;
end;
Như trên, chúng ta triển khai phương thức GetName tùy chỉnh cho thuộc tính Name. Nghĩa là mỗi khi thuộc tính Name được gọi, phương thức GetName sẽ được thực thi và trả về giá trị cho thuộc tính Name. Đây gọi là Getter.
Lưu ý: Getter không được có tham số. Setter thì có thể được. Getter chỉ là một hàm, Setter là một thủ tục.
Lợi ích của getter là gì ? Nó cho phép chúng ta kiểm soát thông tin. Như ví dụ trên, getter GetName chỉ trả về giá trị FName khi trường IsNguoiQuen (là người quen) là true. Đối với setter ý nghĩa tương tự.
Ví dụ khác về setter và getter.
type
TBook = class
IsTacGia: boolean;
private
FName: string;
function GetName: string;
procedure SetName(NewName: string);
published
property Name: string read GetName write SetName;
end;
function TBook.GetName: string;
begin
Result := FName;
end;
procedure TBook.SetName(NewName: string);
begin
if IsTacGia = true then
FName := NewName;
end;
Phương thức SetName được gọi là Setter. Nó cho phép việc kiểm soát sự thay đổi thông tin, chẳng hạn như trong ví dụ trên chỉ thay đổi được FName khi trường IsTacGia là true (là tác giả).
Lưu ý: Setter cần có một tham số có cùng kiểu với kiểu của thuộc tính.
Từ khóa default trong Delphi mang hai ý nghĩa:
1. Đánh dấu giá trị mặc định cho thuộc tính: ví dụ như
property Name: string read FName write FName; default 'Nguyen Van A';
Lưu ý: default không phải là một phần của khai báo thuộc tính, cần được ngăn cách ra bởi dấu chấm phẩy như trên. Hằng số phía sau default cần có cùng kiểu với thuộc tính.
2. Đặt thuộc tính là mặc định để truy cập nhanh như một biến:
Default trong trường hợp này không cần tham số theo sau. Chỉ có mỗi từ khóa default và dấu chấm phẩy.
Ví dụ dễ thấy nhất là trong lớp TStringList được xây dựng sẵn, cơ bản như sau
type
TStringList = class
...
published
property Strings[Index: Integer]: string
read GetString write SetString; default;
end;
var
MyList: TStringList;
Nếu không có thuộc tính default, ta phải truy cập MyList theo cách thông thường, nghĩa là theo kiểu MyList.Strings[1]
. Như thế sẽ dài dòng.
Nhờ có từ khóa default, ta có thể truy cập nhanh bằng cách MyList[1]
để lấy nhanh chuỗi thứ hai trong MyList (đếm từ 0).
Lưu ý: Mỗi class chỉ có 1 thuộc tính default.
Từ khóa Index xác định thứ tự của một thuộc tính, thay đổi cấu trúc các getter và setter, thích hợp với xử lí với các trường dạng mảng.
Ví dụ đoạn code bên dưới xử lí một đối tượng TBox với ba thuộc tính Dai, Rong, Cao. Thay vì lưu trữ trong 3 trường FDai, FRong, FCao, ta sử dụng một mảng FSize gồm 3 phần tử để lưu:
type
TBox = class
private
FSize: array [1..3] of Integer;
function GetSize(Index: Integer): Integer;
procedure SetSize(Index: Integer; GiaTri: Integer);
published
property Dai: Integer index 1 read GetSize write SetSize;
property Rong: Integer index 2 read GetSize write SetSize;
property Cao: Integer index 3 read GetSize write SetSize;
end;
function TBox.GetSize(Index: Integer): Integer;
begin
Result := FSize[Index];
end;
procedure TBox.SetSize(Index: Integer; GiaTri: Integer);
begin
FSize[Index] := GiaTri;
end;
Các setter SetSize và getter GetSize đã được thay đổi danh sách tham số, tham số Index được thêm vào đầu danh sách. Điều này giúp ta dễ dàng hơn trong việc thao tác với các fields dạng mảng.
Bây giờ, chúng ta nên mở rộng class, cho phép truy cập trực tiếp bằng cách thêm một property.
property Size[Index: Integer]: Integer read GetSize write SetSize;
Thế là hoàn thành. Bây giờ bạn có thể sử dụng kết hợp giữa các thuộc tính Dai, Rong, Cao và trực tiếp qua đối tượng Box1 (được tạo ra từ class TBox bằng phương thức Create)
var
Box1: TBox;
begin
Box1 := TBox.Create;
Box1.Dai := 5;
Box1[2] := 4;
// Tương tự với Box1.Rong := 4;
Box1.Size[3] := 3;
// Tương tự với Box1.Cao := 3;
WriteLn(Box1.Size[1]);
// Tương tự với WriteLn(Box1.Dai);
WriteLn(Box1[2]);
// Tương tự với WriteLn(Box1.Rong);
WriteLn(Box1.Cao);
end.
Thực ra đây là một từ khóa không quan trọng và ít được sử dụng bởi những lập trình viên Delphi không chuyên.
Từ khóa stored là một phần trong khai báo property, tương tự như read và write, không cần ngăn cách bằng chấm phẩy như default.
property Visible: boolean read FVisible write FVisible stored true;
Thuộc tính Visible (hiển thị) có chứa từ khóa stored là true, có nghĩa là mọi thứ thuộc class này, thuộc tính Visible được lưu trong tệp .DFM (tệp cấu trúc form).
Mặc định nếu không có stored nghĩa là stored là true.
Có thể đặt stored là false cho một số thuộc tính không quan trọng, để giảm bớt dung lượng cho file .DFM.
Được sử dụng phổ biến tron các class của VCL component, như TButton (nút bấm). Khi tạo một ứng dụng VCL Forms, TButton có nhiều thuộc tính, nhưng mở file .dfm lên sẽ thấy chỉ một số ít thuộc tính quan trọng của TButton được lưu, còn lại bị bỏ qua vì stored là false.