Khái niệm về Annotation (Attribute)
Một thuộc tính chú thích (Annotation / Attribute) tác động vào một thành phần nào đó của chương trình
(lớp, phương thức, thuộc tính) nó là một phần của
siêu dữ liệu
(metadata - loại dữ liệu cung cấp thêm thông tin về đối tượng nào đó).
Annotation giúp thêm thông tin vào lớp, phương thức, thuộc tính những đoạn code mở rộng.
Tính năng này trong Java gọi là Annotation, trong C# gọi là Attribute.
Các thuộc tính chú thích có thể được truy xuất tra cứu ở thời điểm thực thi bằng kỹ thuật
gọi là reflection
, truy xuất ngược từ đối tượng biết được nguồn gốc mà đối tượng đó sinh ra (lớp).
Trong C# có định nghĩa sẵn vô số các Attribute, để sử dụng nó bạn chỉ cần viết
tên Attribute trong dấu []
trước đối tượng áp dụng như lớp, phương thức, thuộc tính lớp
(có tham số như hàm, nếu Attribute đó yêu cầu).
[AttributeName(param1, param2 ...)]
Ví dụ: trong C# có thuộc tính Obsolete
,
để đánh dấu một phương thức, lớp ... nào đó là lạc hậu. Có nghĩa là thêm thông tin cho trình biên dịch
biết một thành phần nào đó là đã lạc hậu, ví dụ:
public class MyClass { [Obsolete ("Phương thức này lỗi thời, hãy dùng phương thức Abc")] public static void Method1 () { Console.WriteLine ("Phương thức chạy"); } }
Đã đánh dấu phương thức Method1 là lạc hậu, nếu còn sử dụng sẽ được trình biên dịch cảnh báo.
Kết quả là khi soạn code, hay khi biên dịch sẽ có cảnh báo.
Attribute không chỉ là thêm thông tin sử dụng bởi trình biên dịch, mà nó thực sự thêm các đặc tính mà code sẽ đọc được ở thời điểm thực thi.
Tự tạo Attribute C#
Để tạo Attribute riêng khá đơn giản, bạn chỉ việc tạo một lớp kế thừ từ System.Attribute
. Tuy nhiên để sử dụng được trước tiên cần thành thạo kỹ thuật
Reflection
trong C#. Ở mức độ đơn giản, hãy tìm hiểu và sử dụng Type
tại
Đọc thuộc tính lớp với Type
. Giờ thử thực hành tạo một Attribute riêng đặt tên là MotaAttribute
,
thuộc tính này để bạn thêm thông tin là một chuỗi (string) mô tả nào đó cho thành phần áp dụng
(như lớp, thuộc tính, phương thức)
using System; namespace CS026_Attribute { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Method)] public class MotaAttribute : Attribute // có thể đặt tên Mota thay cho MotaAttribute { // Phương thức khởi tạo public MotaAttribute(string v) => Description = v; public string Description {set; get;} } }
Trong định nghĩa trên, [AttributeUsage( ... )]
là để thiết lập thuộc tính cho
Mota
, cho biết thuộc tính này áp dụng được cho những thành phần nào, như ví dụ trênn:
nghĩa là được phép áp dụng cho các thuộc tính lớp.
- có
AttributeTargets.Property
có nghĩa áp dụng được cho thuộc tính của lớp - có
AttributeTargets.Class
có nghĩa áp dụng được cho lớp - có
AttributeTargets.Method
thì áp dụng được cho phương thức
Như vậy bạn đã tạo được một thuộc tính Mota, áp dụng cho đối tượng nào thì chỉ cần viết
[Mota("mô tả gì đó")]
Thì đối tượng đó có thêm thông tin Description với nội dung do bạn thêm vào.s
Áp dụng Attribute Mota
User.cs
using System; namespace CS026_Attribute { [Mota("Lớp biểu diễn người dùng")] // thêm Attribute cho lớp public class User { [Mota("Thuộc tính lưu tuổi")] // thêm Attribute cho thuộc tính lớp public int age {set; get;} [Mota("Phương thức này hiện thị age")] // thêm Attribute cho phương thức public void ShowAge() {} } }
Thông tin thêm vào bởi Attribute có thể đọc được bằng kỹ thuật Reflection (đọc thêm và Type - Reflect)
Để đọc Attribute sử dụng Type
trên đối tượng với phương thức GetCustomAttributes
using System; namespace CS026_Attribute { class TestReadAttribute { public static void test () { var a = new User (); // Đọc các Attribute của lớp foreach (Attribute attr in a.GetType ().GetCustomAttributes (false)) { MotaAttribute mota = attr as MotaAttribute; if (mota != null) { Console.WriteLine ($"{a.GetType().Name,10} : {mota.Description}"); } } // Đọc Attribute của từng thuộc tính lớp foreach (var thuoctinh in a.GetType ().GetProperties ()) { foreach (Attribute attr in thuoctinh.GetCustomAttributes (false)) { MotaAttribute mota = attr as MotaAttribute; if (mota != null) { Console.WriteLine ($"{thuoctinh.Name,10} : {mota.Description}"); } } } // Đọc Attribute của phương thức foreach (var m in a.GetType ().GetMethods ()) { foreach (Attribute attr in m.GetCustomAttributes (false)) { MotaAttribute mota = attr as MotaAttribute; if (mota != null) { Console.WriteLine ($"{m.Name,10} : {mota.Description}"); } } } } } }
Chạy, đọc được thông tin:
User : Lớp biểu diễn người dùng age : Thuộc tính lưu tuổi ShowAge : Phương thức này hiện thị age
Chú ý, các phương thức GetCustomAttributes()
của Reflection
sử dụng
ở trên lấy Attribute được thêm vào.
Chạy code trên, kết quả:
Data Annotation/Attribute trong C#
Các Data Annotation/Attribute trong C# định nghĩa trong namespace
System.ComponentModel.DataAnnotations
, có các loại gồm:
- Các Attribute để kiểm tra dữ liệu (Validation Attribute)
- Các Attribute hiện thị (Display Attribute), điều khiển dữ liệu trong lớp hiện thị thế nào trong UI
- Modelling Attribute
Hãy thử xem một vài Attribute có sẵn:
Required |
Dữ liệu phải được thiết lập (!=null)
[Required (ErrorMessage="{0} cần thiết lập")] |
StringLength |
Thiết lập độ dài trường dữ liệu
[StringLength (20,MinimumLength=3, ErrorMessage="Phải dài 3 đến 20 ký tự")] |
DataType |
Chỉ ra dữ liệu phải liên kết phù hợp với một kiểu nào đó
[DataType(DataType.Text)] [DataType(DataType.PhoneNumber)] [DataType(DataType.EmailAddress)] /.. Date, DateTime, Html, ImageUrl, MultilineText, Password, Time, Url |
Range |
Chỉ ra dữ liệu phải trong khoảng nào đó
[Range(18,99, ErrorMessage="Tuổi từ 18 đến 99")] [Range(typeof(DateTime), "1/2/2004", "3/4/2004", ErrorMessage = "Value for {0} must be between {1} and {2}")] |
Phone |
[Phone] dữ liệu phải là dạng số điện thoại |
EmailAddress |
[EmailAddress] dữ liệu phải là dạng email |
Ví dụ:
Employer.csusing System; using System.ComponentModel.DataAnnotations; namespace CS026_Attribute { public class Employer { [Required (ErrorMessage = "Employee {0} is required")] [StringLength (100, MinimumLength = 3, ErrorMessage = "Tên từ 3 đến 100 ký tự")] [DataType (DataType.Text)] public string Name { get; set; } [Range (18, 99, ErrorMessage = "Age should be between 18 and 99")] public int Age { get; set; } [DataType (DataType.PhoneNumber)] [Phone] public string PhoneNumber { set; get; } [DataType (DataType.EmailAddress)] [EmailAddress] public string Email { get; set; } } }
Để kiểm tra các dữ liệu phù hợp thiết lập bởi Attribute,
thì dùng lớp ValidationContext
(System.ComponentModel.DataAnnotations), ví dụ:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; // ... public static void checkValidationContext() { Employer user = new Employer(); user.Name = "AF"; user.Age = 6; user.PhoneNumber = "1234as"; user.Email = "test@re"; ValidationContext context = new ValidationContext(user, null, null); // results - lưu danh sách ValidationResult, kết quả kiểm tra List<ValidationResult> results = new List<ValidationResult>(); // thực hiện kiểm tra dữ liệu bool valid = Validator.TryValidateObject(user, context, results, true); if (!valid) { // Duyệt qua các lỗi và in ra foreach (ValidationResult vr in results) { Console.ForegroundColor = ConsoleColor.Blue; Console.Write($"{vr.MemberNames.First(), 13}"); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($" {vr.ErrorMessage}"); } } } // ...
Mã nguồn CS026_Attribute hoặc tải về cs026