В основу публикации положена статья Dieter Deysel Using Attributes, Reflection and Generics To Increase The Usability Of Enums.
Данная статья описывает решение, используемое нашей командой в разрабатываемых проектах. В решении используются кастомные аттрибуты, механизм отражения и дженерики. С их помощью мы получаем максимальную отдачу от использования перечислений, наделяя каждый элемент перечисления дополнительными метаданными. Надеюсь многим будет интересно познакомиться с нашим решением. Уверен, оно позволит упростить и улучшить разрабатываемый код.
В текущем проекте нам приходится использовать большое количество разнообразным перечислений различных типов. Использование перечислений само по себе очень упрощает код и избавляет от всякого хардкода. Однако, иногда элементы перечисления очень хочется дополнить экстра данными, как например строковое описание элемента или что то еще. Ниже я хочу продемонстрировать, как мы решили этот момент.
Многие разработчики наверняка видели реализации подобной функциональности с использованием самодельного аттрибута EnumDescription, который может к элементу перечисления привязать строковое описание поясняющее его суть. Вот пример такого использования аттрибута:
public enum Institution
{
[EnumDescription(“Bank A”)]
BankA,
[EnumDescription (“Shop B”)]
ShopB,
[EnumDescription(“Library C”)]
LibraryC
}
Наше решение основано на похожей концепции. Мы используем расширенные аттрибуты, которые содержат не просто одно строковое поле для описания элемент, а множество всех необходимых свойств. Помимо этого мы используем не один, а несколько кастомных аттрибутов, для семантического разделения их предназначений. Такой подход, позволяет более гибко декорировать те или иные элементы перечислений.
Вот пример реализации наших аттрибутов:
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class InstituteAttribute: Attribute
{
public InstituteAttribute(string Desc, string Phone)
{
Description = Desc;
Telephone=Phone;
}
public string Description {get;set;}
public string Telephone {get;set;}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class AddressAttribute: Attribute
{
public AddressAttribute (string Add1, string Add2, string City, string ZipCode)
{
this.Address1 = Add1;
this.Address2 = Add2;
this.City = City;
this.ZipCode = ZipCode;
}
public string Address1 {get;set;}
public string Address2 {get;set;}
public string City {get;set;}
public string ZipCode {get;set;}
}
Для того что бы связать дополнительные данные с элементом перечисления, мы используем подход схожий с тем, который который применяется в случае описанном выше с аттрибутом EnumDescription. Но применение наших аттрибутов не является таким же гибким, так как мы должны знать, с каким типом мы работаем, и какие свойства аттрибутов доступны.
Вот пример использования наших аттрибутов.
public enum Institution
{
[Institute (“Bank A”, ”(856) 745-1546”)]
[Address(“123 Street”, “Some town”, “Some City” , ”1452”)]
BankA,
[Institute (“Shop B”, “(012) 154-1456”)]
ShopB,
[Institute (“Library C”, “(123) 147-4567”)]
LibraryC
}
Для того что бы получить данные из аттрибутов, мы реализовали метод расширения, который используя средства рефлексии, получает дополнительую информацию из свойств аттрибутов, которые есть у данного элемента перечисления.
Ниже представлена реализация подобного метода:
///
/// Use this extension method to obtain any property in an attribute used to decorate the fields of an Enum.
///
/// Type of attribute containing the wanted properties
/// This will be the Enum value
/// The property of the specified attribute that must be returned.
/// The property value specified
public static string GetPropertyValue(this Enum value, string AttributeProperty)
{
//Get the enum value
FieldInfo fi = value.GetType().GetField(value.ToString());
T[] atts = fi.GetCustomAttributes(typeof(T), false) as T[];
//if there is any attributes of the specified type
if (atts.Length == 0)
return string.Empty;
//find propertyname
PropertyInfo pi = atts[0].GetType().GetProperty(AttributeProperty);
if (pi == null)
throw new ArgumentException(String.Format("{0} is not a valid property name.", AttributeProperty));
//return value of property
return pi.GetValue(atts[0],null).ToString();
}
Теперь, обладая подобным методом, мы без труда можем получить дополнительные данные элемента перечисления.
Ниже представлен пример получения данных:
string shopPhone = Institution.ShopB.GetPropertyValue(“Telephone”);
Выражение в примере выше вызовет метод расширения GetPropertyValue для элемента перечисления ShopB и вернет значение свойства Telephone аттрибута Institute, который есть у элемента ShopB.
Вот и все. Надеюсь описанное здесь решение окажется полезным и для вас.
Комментариев нет:
Отправить комментарий