C# – Tìm hiểu về các Attribute thông dụng trong .Net

Attribute là một cơ chế được dùng để định nghĩa và khai báo các thông tin cần thiết, nhằm bổ sung và hỗ trợ khá nhiều chức năng liên quan đến việc soạn thảo, debug, cũng như biên dịch các chương trình.

Các attribute được sử dụng bằng cách đặt phía trên các thành phần (assembly, lớp, phương thức, property…) với cặp ngoặc vuông [] bao lại. Nhiều attribute có thể được sử dụng trong cùng một cặp ngoặc vuông  này và được ngăn cách nhau bởi dấu phẩy “,”.

Trong bài viết này tôi chỉ giới thiệu một số attribute thông dụng. Danh sách các attribute trong .Net khá dài, bạn có thể tìm kiếm và khám phá chúng trong thư viện MSDN của Microsoft tại link bên dưới.

http://msdn.microsoft.com/en-us/library/system.attribute.aspx

Obsolete attribute

Namespace: System.

Attribute này chỉ có một mục đích duy nhất là đánh dấu các phương thức hoặc thành phần nào đó không nên được sử dụng nữa, thông thường attribute này sẽ chỉ ra một phương pháp thay thế cho thành phần bị “đào thải” này. Thay vì xóa hẳn thành phần này đi ta vẫn giữ lại và “đánh dấu” nó để các dự án liên quan không xảy ra các lỗi về tương thích phiên bản.

Khi sử dụng các phương thức bị đánh dấu với attribute này, bạn sẽ thấy chuỗi “[deprecated]” phía trước định nghĩa của chúng cùng với 1 warning khi biên dịch.

Các overload của constructor lớp ObsoleteAttribute:
ObsoleteAttribute()

ObsoleteAttribute(String message)

ObsoleteAttribute(String message, Boolean isError)

Ví dụ về cách gán attribute này cho một phương thức như sau:

class Program
{
	[Obsolete()]
	class MyClass
	{

	}

	static void Main(string[] args)
	{
		new MyClass();  //warning
		Print("Test"); //warning
		Console.Read();
	}

	[Obsolete("Do not use this method")]
	public static void Print(string message)
	{
		Console.WriteLine(message);
	}
}

Khi biên dịch đoạn mã trên bạn sẽ nhận được 2 Warning trong cửa sổ Error List

1 | ‘ConsoleApplication1.Program.MyClass’ is obsolete

2 | ‘ConsoleApplication1.Program.Print(string)’ is obsolete: ‘Do not use this method’

Nếu bạn dùng gán tham số isError bằng true thì thay vì warning, trình biên dịch sẽ thay thế bằng một thông báo error tức là bạn không thể biên dịch được.

[Obsolete("Do not use this method",true)]
public static void Print(string message)
{
	Console.WriteLine(message);
}

Chú ý: Bạn không thể dùng attribute này với các phương thức override, virtual hoặc abstract.

Conditional attribute

Namespace: System.Diagnostics.

Attribute này được sử dụng để tạo ra các conditional method. Dựa vào điều kiện của các attribute mà conditional method có được biên dịch hay không. Để định nghĩa kí hiệu mà ta sử dụng trong Conditional attribute, ta sử dụng tiền chỉ thị #define, để hủy định nghĩa ta dùng tiền chỉ thị #undef.

Các chỉ thị trên phải được đặt ở đầu mỗi tập tin mà conditional method được sử dụng. Nếu bạn sử dụng nhiều Conditional attribute thì phương thức sẽ được thực thi khi một trong các điều kiện được đáp ứng:

Hãy xem ví dụ sau:

#define CONDITION1

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
	class Program
	{

		static void Main(string[] args)
		{
			PrintA("Test A");
			new Program().PrintB("Test Program B");
			new NewProgram().PrintB("Test New Program B");
			Console.Read();
		}

		[Conditional("CONDITION1")]
		public static void PrintA(string message)
		{
			Console.WriteLine(message);
		}

		[Conditional("CONDITION1"), Conditional("CONDITION2")]
		public virtual void PrintB(string message)
		{
			Console.WriteLine(message);
		}
	}
	class NewProgram : Program
	{
		public override void PrintB(string message)
		{
			base.PrintB(message);
		}
	}
}

Kết quả xuất ra:

Test A
Test Program B
Test NewProgram B

Ví dụ trên cho ta một cái nhìn đầy đủ về conditional attribute. Bạn có thể thấy là phương thức PrintB() của lớp NewProgram được override từ PrintB() của lớp Program. Và theo một cách không tường minh thì phương thức PrintB() của NewProgram cũng trở thành một conditional method. Có nghĩa là nếu bạn bỏ tiền chỉ thị #define CONDITION1 đi thì cả 3 phương thức được gọi trong Main() đều không được thực thi.

Chú ý:

Conditional method phải có kiểu trả về là void và cũng không phải là phương thức implement từ một interface.

Không thể dùng từ khóa overide và abstract cho conditional method.

Nếu phương thức của bạn là static thì bạn sẽ không thể dùng từ khóa virtual cho phương thức này.
DllImport attribute

Attribute này có tác dụng khai báo một phương thức được lấy ra từ các thư viện liên kết động (Dynamic link library – DLL). Attribute này là một phương pháp để làm việc với các unmanaged-code trong .Net (một cách để kết hợp unmanaged-code và managed-code), bằng cách sử dụng attribute này bạn có thể giải quyết được nhiều vấn đề liên quan đến hệ thống mà đối với .Net gần như là không thể.

Trước đây tôi cũng từng sử dụng attribute này để làm việc với thư viện winmm.dll (coi tại đây). Tôi sẽ giới thiệu lại bằng cách làm một ví dụ nhỏ để chương trình của bạn có thể mở ổ đĩa CD ra.

class Program

{

[DllImport("winmm.dll", EntryPoint = "mciSendStringA", CharSet = CharSet.Ansi)]

protected static extern int mciSendString(string lpstrCommand, string lpstrReturnString, int uReturnLength, IntPtr hwndCallback);

static void Main(string[] args)

{

mciSendString("set cdaudio door open", null, 0, IntPtr.Zero);

Console.Read();

}

}

Để đóng ổ CD-ROM lại thay thế tham số đầu tiên trong phương thức mciSendString bằng “set cdaudio door closed“.

Các thuộc tính thường sử dụng của attribute này gồm có:

– Value: tên của file DLL chứa phương thức cần sử dụng, thông thường chúng nằm trong thư mục hệ thống của Windows hoặc thư mục hiện tại. Bạn có thể dùng đường dẫn tuyệt đối để chỉ đến file DLL đó.

– Charset: thuộc tính kiểu enum này chỉ ra kiểu của các chuỗi được sử dụng trong phương thức. Enum Charset này chi có 4 giá trị là: None, Ansi, Unicode và Auto. Giá trị None tương tự với Ansi và được coi là obsolete. Giá trị Auto sẽ dựa vào hệ điều hành để xác định xem Charset sẽ là Ansi hay Unicode.

– EntryPoint: Xác định “điểm vào” tức là tên phương thức trong thư viện sẽ được gọi.

– SetLastError: Khi làm việc với unmanaged-code bạn không thể lấy được các exception như trong managed-code, thay vào đó ta phải gọi hàm GetLastWin32Error. Và muốn nhận được lỗi trong trường hợp nó xảy ra, bạn phải đặt SetLastError bằng true.

Một ví dụ khác để hiển thị MessageBox trong dự án Console của bạn:

class Program

{

[DllImport("user32.dll",EntryPoint = "452")]

public static extern int MyMessageBox(IntPtr hWnd, String text, String caption, uint type);

static void Main(string[] args)

{

MyMessageBox(IntPtr.Zero, "Welcome to YinYang's Blog", "Welcome", 0);

Console.Read();

}

}

Trong phần này tôi không giải thích ý nghĩa và cách làm việc với các hàm Windows API, nó là một lĩnh vực tương đối rộng với rất nhiều hàm. Bạn có thể vào trang http://pinvoke.net/ để tìm kiếm và xem ví dụ về cách sử dụng hàm này trong .Net.
Một số Attribute thông dụng khác

Bạn có thể học cách làm việc với các attribute dễ dàng trên trang MSDN thông qua các giải thích và ví dụ cụ thể. Có một số attribute rất hữu ích khác mà tôi giới hạn không trình bày trong bài viết, tuy nhiên có thể bạn sẽ thấy hứng thú với nó sau khi thử qua. Đặc biệt namespace System.ComponentModel chứa các attribute nên được bạn xem xét trước.

DebuggerDisplayAttribute: Cho phép bạn chỉ ra các thông tin cần thiết sẽ hiện thị trên đối tượng khi debug.

FlagsAttribute: phương pháp để một các giá trị của một enum có thể kết hợp với nhau bằng các toán tử thao tác bit.

DescriptionAttribute: Thêm mô tả cho các property hoặc event. Bạn có thể lấy được các thông tin này trong quá trình runtime.

SerializableAttribute: xác định một class có thể được serialized để lưu đối tượng xuống file.

Như vậy bạn đã làm quen và có thể sử dụng được các attribute tạo sẵn của .Net.