Kiểm tra các thuộc tính được thực hiện tự động
Cú pháp tiêu chuẩn để xác định một thuộc tính trong C# bao gồm một get và một người set nhập. Tuy nhiên, khi không cần lô-gic nào khác trong người truy nhập, bạn có thể sử dụng các thuộc tính được triển khai tự động.
Thuộc tính được triển khai tự động
Tự động thực hiện các thuộc tính làm cho khai báo thuộc tính ngắn gọn hơn khi không có lô-gic khác được yêu cầu trong các truy cập thuộc tính. Chúng cũng cho phép mã máy khách tạo đối tượng. Khi bạn khai báo thuộc tính như minh họa trong ví dụ sau đây, trình biên dịch sẽ tạo trường sao lưu riêng tư, ẩn danh chỉ có thể truy nhập thông qua get và set của thuộc tính.
init cũng có thể được khai báo là các thuộc tính được thực hiện tự động.
Ví dụ sau đây cho thấy một lớp đơn giản có một số thuộc tính được thực hiện tự động:
// This class is mutable. Its data can be modified from
// outside the class.
public class Customer
{
// Auto-implemented properties for trivial get and set
public double TotalPurchases { get; set; }
public string Name { get; set; }
public int CustomerId { get; set; }
// Constructor
public Customer(double purchases, string name, int id)
{
TotalPurchases = purchases;
Name = name;
CustomerId = id;
}
// Methods
public string GetContactInfo() { return "ContactInfo"; }
public string GetTransactionHistory() { return "History"; }
// .. Other methods, events, etc.
}
class Program
{
static void Main()
{
// Initialize a new object.
Customer cust1 = new Customer(4987.63, "Northwind", 90108);
// Modify a property.
cust1.TotalPurchases += 499.99;
}
}
Tự động triển khai và các thuộc tính được sao lưu trường khai báo trường sao lưu phiên bản riêng tư. Bạn có thể khởi tạo các thuộc tính được triển khai tự động tương tự như trường:
public string FirstName { get; set; } = "FirstName";
Lớp Customer được hiển thị trong ví dụ trước là mutable. Mã máy khách có thể thay đổi giá trị trong đối tượng sau khi tạo. Trong các lớp phức tạp có chứa hành vi quan trọng (phương pháp) và dữ liệu, thường cần phải có thuộc tính công cộng. Tuy nhiên, đối với các lớp hoặc cấu trúc nhỏ chỉ đóng gói một tập giá trị (dữ liệu) và có ít hoặc không có hành vi nào, bạn nên sử dụng một trong các tùy chọn sau đây để làm cho các đối tượng không thay đổi được:
- Chỉ khai báo một người truy cập get (không thể thay đổi ở mọi nơi ngoại trừ hàm tạo).
- Khai báo một người truy cập có được và một người truy cập init (không thể thay đổi ở khắp mọi nơi ngoại trừ trong quá trình xây dựng đối tượng).
- Khai báo bộ truy nhập là riêng tư (không thể thay đổi đối với người tiêu dùng).
Bạn có thể cần thêm xác thực vào thuộc tính được triển khai tự động. C# 13 thêm các thuộc tính được hỗ trợ của trường dưới dạng một tính năng xem trước. Bạn sử dụng từ khóa trường để truy nhập vào trường sao lưu tổng hợp của thuộc tính được thực hiện tự động. Ví dụ: bạn có thể đảm bảo rằng không thể đặt thuộc tính FirstName trong ví dụ trên thành null hoặc chuỗi trống:
public string FirstName
{
get;
set
{
field = (string.IsNullOrWhiteSpace(value) is false
? value
: throw new ArgumentException(nameof(value), "First name can't be whitespace or null"));
}
} = "FirstName";
Tính năng này cho phép bạn thêm lô-gic cho người truy nhập mà không yêu cầu bạn khai báo rõ ràng trường sao lưu. Bạn sử dụng từ khóa trường để truy nhập trường sao lưu do trình biên dịch tạo ra.
Quan trọng
Từ field là một tính năng xem trước trong C# 13. Bạn phải sử dụng .NET 9 và đặt thành phần <LangVersion> của bạn thành preview trong tệp dự án của bạn để sử dụng từ khóa ngữ cảnh field của bạn.
Bạn nên cẩn thận khi sử dụng tính field từ khóa trong lớp có trường có tên field. Từ khóa field sẽ đổ bóng trường có tên field trong phạm vi của người truy nhập thuộc tính. Bạn có thể thay đổi tên của biến field hoặc sử dụng mã thông báo @ để tham chiếu mã định danh field định danh @field.
Thuộc tính có trường sao lưu
Bạn có thể kết hợp khái niệm của thuộc tính tính toán với trường riêng tư và tạo thuộc được đánh giá trong bộ đệm. Ví dụ: cập nhật thuộc FullName chuỗi để định dạng chuỗi diễn ra trong lần truy nhập đầu tiên:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
Việc thực hiện này hoạt động vì các FirstName và LastName tính có sẵn. Mọi người có thể thay đổi tên của họ. Cập nhật thuộc tính FirstName và LastName cho phép người set truy nhập yêu cầu bạn phải xác thực mọi giá trị được lưu trong bộ đệm ẩn cho fullName. Bạn sửa đổi các set truy cập của FirstName thuộc tính và LastName để fullName trường được tính toán lại:
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
Phiên bản cuối cùng này đánh giá thuộc FullName chỉ khi cần thiết. Phiên bản được tính toán trước đó được dùng nếu hợp lệ. Nếu không, tính toán sẽ cập nhật giá trị được lưu trong bộ đệm ẩn. Các nhà phát triển sử dụng lớp này không cần phải biết chi tiết thực hiện. Không có thay đổi nội bộ nào trong số này ảnh hưởng đến việc sử dụng Person tượng.
Bắt đầu với C# 13, bạn có thể tạo các lớp partial properties một phần. Khai báo triển khai cho thuộc tính partial không thể là thuộc tính được thực hiện tự động. Thuộc tính được thực thi tự động sử dụng cú pháp giống như khai báo thuộc tính một phần khai báo.
Triển khai lớp nhẹ với các thuộc tính được triển khai tự động
Bạn có thể gặp phải các tình huống khi bạn cần tạo một lớp nhẹ không thay đổi chỉ đóng gói một tập hợp các thuộc tính được thực hiện tự động. Sử dụng loại cấu trúc này thay vì struct khi bạn phải sử dụng ngữ ngữ từ loại tham chiếu.
Bạn có thể tạo thuộc tính không thể thay đổi theo những cách sau:
- Chỉ khai báo
gettruy nhập, điều này làm cho thuộc tính không thể thay đổi ở mọi nơi ngoại trừ hàm tạo của kiểu. - Khai báo một
initthay vì một người truyset, mà làm cho thuộc tính chỉ có thể thiết lập trong hàm tạo hoặc bằng cách sử dụng một khởi tạo đối tượng. - Khai báo
settruy nhập làprivate. Thuộc tính này có thể được thiết lập trong loại, nhưng nó không thể thay đổi đối với người tiêu dùng.
Bạn có thể thêm required hiệu ứng vào khai báo thuộc tính để buộc người gọi đặt thuộc tính như một phần của việc khởi tạo đối tượng mới.
Ví dụ sau đây cho thấy cách một thuộc tính chỉ có người truy nhập khác với thuộc tính có được và nhóm riêng tư.
class Contact
{
public string Name { get; }
public string Address { get; private set; }
public Contact(string contactName, string contactAddress)
{
// Both properties are accessible in the constructor.
Name = contactName;
Address = contactAddress;
}
// Name isn't assignable here. This will generate a compile error.
//public void ChangeName(string newName) => Name = newName;
// Address is assignable here.
public void ChangeAddress(string newAddress) => Address = newAddress;
}
Ví dụ sau đây cho thấy hai cách để thực hiện một lớp không thể thay đổi bằng cách sử dụng các thuộc tính được thực hiện tự động. Mỗi cách khai báo một trong các thuộc tính với một tài set và một trong các thuộc tính với một get duy nhất. Lớp đầu tiên sử dụng một hàm xây dựng chỉ để khởi tạo các thuộc tính, và lớp thứ hai sử dụng một phương pháp tĩnh nhà máy mà gọi một hàm xây dựng.
// This class is immutable. After an object is created,
// it can't be modified from outside the class. It uses a
// constructor to initialize its properties.
class Contact
{
// Read-only property.
public string Name { get; }
// Read-write property with a private set accessor.
public string Address { get; private set; }
// Public constructor.
public Contact(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
}
// This class is immutable. After an object is created,
// it can't be modified from outside the class. It uses a
// static method and private constructor to initialize its properties.
public class Contact2
{
// Read-write property with a private set accessor.
public string Name { get; private set; }
// Read-only property.
public string Address { get; }
// Private constructor.
private Contact2(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
// Public factory method.
public static Contact2 CreateContact(string name, string address)
{
return new Contact2(name, address);
}
}
public class Program
{
static void Main()
{
// Some simple data sources.
string[] names = ["Person One","Person Two", "Person Three",
"Person Four", "Person Five"];
string[] addresses = ["123 Main St.", "345 Cypress Ave.", "678 1st Ave",
"12 108th St.", "89 E. 42nd St."];
// Simple query to demonstrate object creation in select clause.
// Create Contact objects by using a constructor.
var query1 = from i in Enumerable.Range(0, 5)
select new Contact(names[i], addresses[i]);
// List elements can't be modified by client code.
var list = query1.ToList();
foreach (var contact in list)
{
Console.WriteLine("{0}, {1}", contact.Name, contact.Address);
}
// Create Contact2 objects by using a static factory method.
var query2 = from i in Enumerable.Range(0, 5)
select Contact2.CreateContact(names[i], addresses[i]);
// Console output is identical to query1.
var list2 = query2.ToList();
// List elements can't be modified by client code.
// CS0272:
// list2[0].Name = "Person Six";
}
}
/* Output:
Person One, 123 Main St.
Person Two, 345 Cypress Ave.
Person Three, 678 1st Ave
Person Four, 12 108th St.
Person Five, 89 E. 42nd St.
*/
Trình biên dịch tạo ra các trường sao lưu cho mỗi thuộc tính được thực hiện tự động. Không thể truy nhập trực tiếp các trường từ mã nguồn.