ساخت Attribute سفارشی در #C: افزودن متادیتا به کد
قبل از هر چیزی باید اعتراف کنم یکی از موضوعات مورد علاقه من همین بحث Customized Attribute هست. علتش هم اینکه میتونیم نوشتن کدهایی یکسان با ساده روش ممکن بر روی Mini-API یا API و ... ازش استفاده کنیم و کلی از زمان صرفه جویی کنیم.
Attribute چیست؟
در هسته خود، یک Attribute صرفاً یک کلاس است که اطلاعاتی اضافی را حمل میکند. نمونههایی از Attributeهای پیشفرض شامل [Serializable], [Obsolete], و [TestMethod] (در فریمورکهای تست) هستند.
چرا Attribute سفارشی میسازیم؟
در بسیاری از سناریوها، نیاز به افزودن اطلاعات خاص به کد داریم که Attributeهای پیشفرض نمیتوانند آن را پوشش دهند. ساخت Attribute سفارشی ما را قادر میسازد تا:
-
اعتبارسنجی (Validation) سفارشی: مانند چک کردن فرمت ایمیل یا اعتبار کد ملی.
-
کنترل جریان (Control Flow): مانند محدود کردن دسترسی متدها (در ASP.NET Core).
-
سریالسازی (Serialization): اضافه کردن دستورالعملهای خاص برای نحوه تبدیل یک شیء به فرمت دیگر.
-
مستندسازی (Documentation): افزودن اطلاعات نویسنده، تاریخچه تغییرات، یا سطح اهمیت.
گامهای ساخت یک Attribute سفارشی
برای تعریف یک Attribute سفارشی، باید یک کلاس ایجاد کرده و آن را از کلاس پایه System.Attribute به ارث ببریم. معمولاً نام این کلاس را با پسوند Attribute (صفت) خاتمه میدهیم.
۱. تعریف کلاس Attribute
هر کلاس Attribute سفارشی باید از System.Attribute ارثبری کند.
using System;
// نام Attribute سفارشی ما
public class AuthorAttribute : Attribute
{
// فیلدهای خصوصی برای ذخیره دادهها
private readonly string _authorName;
private double _version;
// سازنده (Constructor) برای پارامترهای اجباری (Positional Parameters)
public AuthorAttribute(string name)
{
_authorName = name;
_version = 1.0; // مقدار پیشفرض
}
// پراپرتی فقط خواندنی برای دسترسی به نام نویسنده
public string Name
{
get { return _authorName; }
}
// پراپرتی خواندنی و نوشتنی برای پارامترهای اختیاری (Named Parameters)
public double Version
{
get { return _version; }
set { _version = value; }
}
}
۲. تعریف محدوده اعمال با [AttributeUsage]
با استفاده از Attribute پیشفرض [AttributeUsage], میتوانیم مشخص کنیم که Attribute سفارشی ما بر روی کدام عناصر زبانی قابل اعمال است. این Attribute دارای سه پارامتر کلیدی است:
| پارامتر | نوع | توضیح |
| AttributeTargets | Enum | مشخص میکند Attribute میتواند روی چه عناصری (کلاس، متد، پراپرتی، و غیره) اعمال شود. مقادیر با عملگر ` |
| AllowMultiple | bool | اگر true باشد، میتوان Attribute را چندین بار روی یک عنصر اعمال کرد. |
| Inherited | bool | اگر true باشد، Attribute به کلاسهای فرزند یا متدهای Override شده منتقل میشود. |
مثال استفاده:
// این Attribute فقط روی کلاسها و متدها قابل اعمال است و میتواند چند بار استفاده شود.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class ChangeLogAttribute : Attribute
{
// ... محتوای Attribute
}
۳. اعمال Attribute سفارشی
Attribute سفارشی با قرار گرفتن در براکت ([]) بالای عنصر مورد نظر اعمال میشود.
// اعمال با پارامترهای اجباری و اختیاری
[Author("علی کریمی", Version = 2.0)]
public class DataProcessor
{
[Author("سارا احمدی")] // اعمال فقط با پارامتر اجباری
public void ProcessData()
{
// ...
}
}
نکته: هنگام استفاده، نیازی به ذکر پسوند Attribute نیست (میتوان نوشت [Author(...)] به جای [AuthorAttribute(...)]).
استفاده از Reflection برای خواندن Attributeها
ساخت Attribute تنها نیمی از ماجرا است. برای استفاده واقعی از متادیتای افزوده شده، باید در زمان اجرا، با استفاده از System.Reflection، آن را بازیابی و تحلیل کنیم.
۱. دسترسی به اطلاعات نوع (Type)
ابتدا باید به نمونهای از Type عنصر مورد نظر دسترسی پیدا کنیم.
Type type = typeof(DataProcessor);
۲. بازیابی Attributeها
متد GetCustomAttributes اصلیترین راه برای خواندن Attributeها است.
using System.Reflection;
using System.Linq;
// ...
Type type = typeof(DataProcessor);
// بازیابی تمام نمونههای AuthorAttribute که مستقیماً روی کلاس اعمال شدهاند
object[] attributes = type.GetCustomAttributes(typeof(AuthorAttribute), false);
foreach (AuthorAttribute attr in attributes.Cast<AuthorAttribute>())
{
Console.WriteLine($"نویسنده: {attr.Name}, نسخه: {attr.Version}");
}
مثال: خواندن Attribute از یک متد
برای دسترسی به Attributeهای یک متد، ابتدا باید به MethodInfo آن متد دسترسی پیدا کنیم:
MethodInfo methodInfo = type.GetMethod("ProcessData");
if (methodInfo != null)
{
AuthorAttribute methodAttr = (AuthorAttribute)Attribute.GetCustomAttribute(methodInfo, typeof(AuthorAttribute));
if (methodAttr != null)
{
Console.WriteLine($"نویسنده متد: {methodAttr.Name}");
}
}
سناریوهای کاربردی پیشرفته
۱. Attribute اعتبارسنجی (Validation Attribute)
در فریمورکهایی مانند ASP.NET Core، میتوان برای اعتبارسنجی ورودی کاربر از Attributeهای سفارشی استفاده کرد. برای این کار، Attribute باید از کلاس ValidationAttribute ارثبری کند و متد IsValid را پیادهسازی نماید.
using System.ComponentModel.DataAnnotations;
public class CustomEmailValidationAttribute : ValidationAttribute
{
// پیادهسازی متد IsValid
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null && value.ToString().Contains("@"))
{
return ValidationResult.Success;
}
// پیغام خطای سفارشی
return new ValidationResult(ErrorMessage ?? "فرمت ایمیل نامعتبر است.");
}
}
نحوه استفاده:
public class UserProfile
{
[Required]
[CustomEmailValidation]
public string Email { get; set; }
}
۲. Attributeهای جنریک (Generic Attributes) (از C# 11 به بعد)
C# 11 امکان تعریف Attributeهایی با پارامترهای نوع عمومی (<T>) را معرفی کرد که انعطافپذیری بیشتری در زمان کامپایل فراهم میکند.
// Attribute جنریک
public class InjectServiceAttribute<T> : Attribute where T : class
{
// ...
}
// استفاده
public class Controller
{
[InjectService<ILogger>] // نوع به عنوان پارامتر Attribute داده شده
public void HandleRequest()
{
// ...
}
}
نتیجهگیری
Attributeهای سفارشی در C# ابزاری اساسی در توسعه داتنت هستند که امکان افزودن متادیتا به کد را فراهم میکنند. این مکانیزم به خصوص در فریمورکها برای اعمال رفتار اعلانی (مانند اعتبارسنجی، مسیریابی، و سریالسازی) حیاتی است. با ارثبری از System.Attribute، تعریف محدوده کاربرد با [AttributeUsage], و استفاده از Reflection در زمان اجرا، توسعهدهندگان میتوانند کدهای تمیزتر، قابل نگهداریتر و قابل گسترشتر بنویسند.
0 نظر
هنوز نظری برای این مقاله ثبت نشده است.