Уверен, многие периодически сталкивались с необходимостью указания каких-либо простых свойств для элементов перечислений в C#.
Существуют различные способы решить эту задачу, самым популярным из которых, на мой взгляд, является написание вспомогательного статического метода, принимающего перечисление в качестве параметра и возвращающего результат нужного типа.
Я же хочу предложить вам несколько иной, более универсальный и изящный способ, основанный на атрибутах, методах расширения и рефлексии.
Взгляните, насколько наглядно мы можем описывать атрибуты для элементов перечислений:
… и насколько просто мы можем получать значения этих элементов:
Создадим абстрактный класс BaseAttribute, от которого в дальнейшем будут наследоваться наши собственные атрибуты. Конструктор принимает и размещает объект на хранение. Метод GetValue() возвращает хранимый объект.
Далее нам необходимо создать метод расширения (extension method), который для элемента любого перечисления будет возвращать значение необходимого атрибута. Для этого создадим статический класс EnumAttributesBaseLogic и определим в нем наш метод, который назовем GetAttributeValue. Последний аргумент метода — значение, которое будет возвращено, если переданный элемент перечисления не отмечен переданным атрибутом.
На этом всё! Теперь мы можем создавать необходимые нам атрибуты всего в одну строчку. Взгляните, как просто создать атрибут Вес*:
* Помните, что в качестве аргументов атрибута можно использовать только константы или массивы примитивных типов.
Также просто мы можем создать теперь и метод расширения для чтения значения только что созданного нами атрибута:
Для удобства, я рекоммендую вынести определения собственных атрибутов и статический класс с расширенными методами для чтения атрибутов в отдельный файл:
Рабочий пример можно скачать отсюда:
http://www.googman.ru/sources/enumattributesdemo.zip (9,1 кб)
P.S. Не судите строго — мой первый пост.
Существуют различные способы решить эту задачу, самым популярным из которых, на мой взгляд, является написание вспомогательного статического метода, принимающего перечисление в качестве параметра и возвращающего результат нужного типа.
Я же хочу предложить вам несколько иной, более универсальный и изящный способ, основанный на атрибутах, методах расширения и рефлексии.
Атрибуты предоставляют эффективный метод связывания декларативной информации с кодом C# (типы, методы, свойства и т. д.). Атрибут, связанный с сущностью программы, может быть запрошен во время выполнения с помощью метода, называемого отражением.
© MSDN, http://msdn.microsoft.com/ru-ru/library/z0w1kczw.aspx
Рефлексию (отражение) используют для динамического создания экземпляра типа, привязки типа к существующему объекту, а также получения типа из существующего объекта и динамического вызова его методов или доступа к его полям и свойствам.
© MSDN, http://msdn.microsoft.com/ru-ru/library/ms173183.aspx
Методы расширения позволяют «добавлять» методы в существующие типы без создания нового производного типа, перекомпиляции или иного изменения исходного типа. Методы расширения являются особым видом статического метода, но они вызываются, как если бы они были методами экземпляра в расширенном типе.
© MSDN, http://msdn.microsoft.com/ru-ru/library/bb383977.aspx
Взгляните, насколько наглядно мы можем описывать атрибуты для элементов перечислений:
public enum Womans
{
[Age(25)]
[Weight(54.5)]
Masha,
[Age(32)]
[Weight(102.5)]
Lena,
[Age(44)]
[Weight(77.4)]
Ira,
[Age(28)]
[Weight(63.75)]
Fekla
}
public enum Status
{
[RussianName("Открыт")]
Opened = 100,
[RussianName("Закрыт")]
Closed = 200,
[RussianName("Что-то еще")]
AnythingElse = 500
}
… и насколько просто мы можем получать значения этих элементов:
double iraWeight = Womans.Ira.GetWeight();
int lenaAge = Womans.Lena.GetAge();
string closedName = Status.Closed.GetRussianName();
Реализация
Создадим абстрактный класс BaseAttribute, от которого в дальнейшем будут наследоваться наши собственные атрибуты. Конструктор принимает и размещает объект на хранение. Метод GetValue() возвращает хранимый объект.
public abstract class BaseAttribute : Attribute
{
private readonly object _value;
public BaseAttribute(object value) { this._value = value; }
public object GetValue() { return this._value; }
}
Далее нам необходимо создать метод расширения (extension method), который для элемента любого перечисления будет возвращать значение необходимого атрибута. Для этого создадим статический класс EnumAttributesBaseLogic и определим в нем наш метод, который назовем GetAttributeValue. Последний аргумент метода — значение, которое будет возвращено, если переданный элемент перечисления не отмечен переданным атрибутом.
public static class EnumAttributesBaseLogic
{
public static VAL GetAttributeValue<ENUM, VAL>(this ENUM enumItem, Type attributeType, VAL defaultValue)
{
var attribute = enumItem.GetType().GetField(enumItem.ToString()).GetCustomAttributes(attributeType, true)
.Where(a => a is BaseAttribute)
.Select(a => (BaseAttribute)a)
.FirstOrDefault();
return attribute == null ? defaultValue : (VAL)attribute.GetValue();
}
}
На этом всё! Теперь мы можем создавать необходимые нам атрибуты всего в одну строчку. Взгляните, как просто создать атрибут Вес*:
public class Weight : BaseAttribute { public Weight(double value) : base(value) { } }
* Помните, что в качестве аргументов атрибута можно использовать только константы или массивы примитивных типов.
Также просто мы можем создать теперь и метод расширения для чтения значения только что созданного нами атрибута:
public static double GetWeight(this Enum enumItem)
{
return enumItem.GetAttributeValue(typeof(Weight), 0m);
}
Для удобства, я рекоммендую вынести определения собственных атрибутов и статический класс с расширенными методами для чтения атрибутов в отдельный файл:
using System;
namespace EnumAttributesDemo
{
public class Age : BaseAttribute { public Age(int value) : base(value) { } }
public class Weight : BaseAttribute { public Weight(double value) : base(value) { } }
public class RussianName : BaseAttribute { public RussianName(string value) : base(value) { } }
public static class EnumExtensionMethods
{
public static int GetAge(this Womans enumItem)
{
return enumItem.GetAttributeValue(typeof(Age), 0);
}
public static double GetWeight(this Womans enumItem)
{
return enumItem.GetAttributeValue(typeof(Weight), 0d);
}
public static string GetRussianName(this Status enumItem)
{
return enumItem.GetAttributeValue(typeof(RussianName), string.Empty);
}
}
}
Рабочий пример можно скачать отсюда:
http://www.googman.ru/sources/enumattributesdemo.zip (9,1 кб)
P.S. Не судите строго — мой первый пост.