اول باید اشاره کنم که علیرغم رجحان خودم، تصمیم گرفتم مطالب بلاگ را با ادبیات رسمی بنویسم تا مترجم ها قادر به ترجمه مقالات باشند.
مواقع بسیار زیادی پیش آمده که نیاز پیدا کردم مقدار Attribute یک شیء (مخصوصا enum) را دریافت کنم. تقریبا همیشه با هدف نمایش به کاربر.
مثلا پروژه ای بود که در آن باید به کاربر امکان فیلتر کردن اطلاعات را می دادیم. برای این کار، کاربر باید از بین عملگرها، یکی را انتخاب می کرد. برای این کار، enum زیر را تعریف کردیم:
public enum Operands
{
[Description("شامل ")]
Like,
[Description("مساوی باشد با ")]
Equal,
[Description("مخالف باشد با ")]
Not,
[Description("بزرگتر باشد از ")]
GreaterThan,
[Description("کوچکتر باشد از ")]
LessThan
}
در مرحله بعد، باید این توضیحات را به کاربر نشان دهیم و هنگام انتخاب، از روی مورد انتخاب شده، متوجه می شویم که مورد انتخاب شده مربوط به کدام enum هست. در تصاویر زیر، هدف نهایی ما از این مقاله نشان داده شده. کنترل DropDownList، مقادیر Description مربوط به enum را نشان می دهد (تصویر 1) و بعد از انتخاب کاربر، مقدار Description و مقدار enum value مربوطه را مشاهده می کنیم. این پروژه را در انتهای این صفحه دانلود کنید.


برای استفاده از Description ها متد زیر را تعریف می کنیم:
public static string GetDescription(this Enum value)
{
var fi = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}
متد GetDescription، یک enum value را دریافت کرده و با استفاده از Reflection، مقدار Description آن را بر می گرداند. این متد، یک Extension Method هست.
برای استفاده کافیست به شکل زیر عمل کنیم:
CustomEnum item = CustomEnum.CustomValue;
string itemDescription = item.GetDescription();
برای انجام معکوس این کار، ابتدا متد زیر را تعریف کردم:
public static T GetEnumValueFromDescription<T>(string Description) where T : struct,IConvertible
{
if (!typeof(T).IsEnum)
{
var errorMessage = string.Format("{0} must be an enumarted type", typeof(T).ToString());
throw new ArgumentException(errorMessage, typeof(T).ToString());
}
foreach (var o in from Enum e in Enum.GetValues(typeof(T))
let sValue = e.GetDescription()
where sValue.ToLower() == Description.ToLower()
select e)
{
return (T)((object)o);
}
return default(T);
}
اما این متد چند مشکل دارد:
1- در حقیقت من یک Workaround برای تعریف T از نوع enum پیدا کرده ام که زیاد قشنگ نیست. استفاده از struct و IConvertible، تا حدی خطا در استفاده را محدود می کند اما در موراد نادری امکان دارد که به Runtime Exception (از نوع ArgumentException) برخورد کنیم.
2- از آنجا که T از نوع class نیست، امکان برگرداندن مقدار null وجود ندارد. از طرفی اگر مقدار بازگشتی را از نوع nullable تعریف کنیم، نیاز به یک cast اضافه در سمت caller خواهیم داشت. بنابراین تنها راه، برگرداندن مقدار (default(T می باشد که در enum ها، تبدیل به مقدار 0 می شود. در نتیجه امکان بازگشتهای False Positive خواهد بود که کاملا غیر قابل قبول است (مثلا یک مقدار string که در enum وجود ندارد را پاس می کنیم اما به جای null، عنصر 0 enum به عنوان مقدار درست بازگشت داده می شود).
به دو دلیل بالا، مجبور شدم قید متدهای Generic را بزنم و گرچه صدا زدن تابع کمی طولانی تر میشد، اما دردسرش بسیار کمتر:
public static Enum GetEnumValueFromDescription(Type MyType, string Description)
{
return (from Enum e in Enum.GetValues(MyType)
let sValue = e.GetDescription()
where sValue.ToLower() == Description.ToLower()
select e).FirstOrDefault();
}
متد GetEnumValueFromDescription، مقدار یک enum را از روی Description بر می گرداند. در صورتیکه مقداری پیدا نشود، مقدار null برگردانده می شود. نحوه استفاده:
var operand = EnumHelpers.GetEnumValueFromDescription(typeof(CustomEnum), customText);
حالا با استفاده از این متدها، می توانیم ابتدا Description ها را به کاربر نشان دهیم و از روی انتخاب کاربر، مقدار enum را تشخیص دهیم. در صورت تمایل، پروژه الحاق شده را برای دیدن مثال اول مقاله دانلود کنید.
644c5706-acec-44a1-af76-75fde49f5882|2|5.0|27604f05-86ad-47ef-9e05-950bb762570c