Я не могу найти хороший способ сделать эту СУХУЮ в .Net Core. (Не повторяйся). Как я могу сделать так, чтобы я не повторял большую часть логики? Вот 2 метода:
public static string GetCategory(this Enum val)
{
CategoryAttribute[] attributes = (CategoryAttribute[])val
.GetType()
.GetField(val.ToString())
.GetCustomAttributes(typeof(CategoryAttribute), false);
return attributes.Length > 0 ? attributes[0].Category : string.Empty;
}
public static string GetDescription(this Enum val)
{
DescriptionAttribute[] attributes = (DescriptionAttribute[])val
.GetType()
.GetField(val.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : string.Empty;
}
Я бы начал с этого:
public static T GetAttribute<T>(this Enum val)
where T : Attribute
{
return (T)val
.GetType()
.GetField(val.ToString())
.GetCustomAttribute(typeof(T), false);
}
Что превращает ваши методы в это:
public static string GetCategory(this Enum val)
{
return val.GetAttribute<CategoryAttribute>()?.Category ?? string.Empty;
}
public static string GetDescription(this Enum val)
{
return val.GetAttribute<DescriptionAttribute>()?.Description ?? string.Empty;
}
Возможно, вы могли бы сделать больше, чтобы сделать эти последние методы немного более СУХИМЫМИ, но я предполагаю, что шаблон, который вы здесь используете (получение свойства из атрибута и возврат его значения или пустой строки), вероятно, недостаточно распространен Стоит создать метод специально для этого. С другой стороны, метод GetAttribute
, вероятно, будет гораздо более GetAttribute
для повторного использования.
Вместо этого вы можете использовать универсальную версию GetCustomAttribute<T>
которая упрощает код до такой степени, что в другой абстракции нет необходимости IMO.
public static string GetCategory(this Enum val)
{
return val.GetType()
.GetField(val.ToString())
.GetCustomAttribute<CategoryAttribute>(false)?.Category ?? string.Empty;
}
public static string GetDescription(this Enum val)
{
return val.GetType()
.GetField(val.ToString())
.GetCustomAttribute<DescriptionAttribute>(false)?.Description ?? string.Empty;
}
false
в этот метод, чтобы поддерживать поведение OP.
В С# 7.3 вы можете ограничить свои методы перечислением типов. Это сэкономит вам один бокс вашего перечисления.
public static class AttributeExtensions
{
public static string GetCategory<T>(this T val) where T: Enum
{
return GetAttr<CategoryAttribute, T>(val)?.Category ?? "";
}
public static string GetDescription<T>(this T val) where T : Enum
{
return GetAttr<DescriptionAttribute, T>(val)?.Description ?? "";
}
private static TAttr GetAttr<TAttr, T>(this T val) where TAttr : Attribute
{
return (TAttr)typeof(T)
.GetField(val.ToString())
?.GetCustomAttributes(typeof(TAttr), false)
?.FirstOrDefault();
}
}
Также при работе с отражением важно кэшировать для производительности:
public static class AttributeExtensions
{
private class EnumMetadata
{
public CategoryAttribute CategoryAttribute { get; set; }
public DescriptionAttribute DescriptionAttribute { get; set; }
}
private class EnumMetadataCache<T> where T : Enum
{
private static readonly ConcurrentDictionary<T, EnumMetadata> MetadataCache = new ConcurrentDictionary<T, EnumMetadata>();
public static EnumMetadata GetMetadata(T item)
{
return MetadataCache.GetOrAdd(item, val =>
new EnumMetadata
{
CategoryAttribute = GetAttr<CategoryAttribute, T>(val),
DescriptionAttribute = GetAttr<DescriptionAttribute, T>(val)
}
);
}
}
public static string GetCategory<T>(this T val) where T : Enum
{
return EnumMetadataCache<T>.GetMetadata(val).CategoryAttribute?.Category ?? "";
}
public static string GetDescription<T>(this T val) where T : Enum
{
return EnumMetadataCache<T>.GetMetadata(val).DescriptionAttribute?.Description ?? "";
}
private static TAttr GetAttr<TAttr, T>(this T val) where TAttr : Attribute
{
return (TAttr)typeof(T)
.GetField(val.ToString())
?.GetCustomAttributes(typeof(TAttr), false)
?.FirstOrDefault();
}
}
TAttribute
параметром типаTAttribute
. Добавьте метод доступа Func в качестве параметра для помощника, вместо того, чтобы пытаться получить свойство напрямую, например,Func<TAttribute,TResult> selector
и передатьatt=>att.Description
при вызове помощника