c # Обобщения для вспомогательных методов Attribute

2

Я не могу найти хороший способ сделать эту СУХУЮ в .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;
    }
  • 0
    Это .NET Framework или .NET Standard / Core?
  • 0
    Замените типы TAttribute параметром типа TAttribute . Добавьте метод доступа Func в качестве параметра для помощника, вместо того, чтобы пытаться получить свойство напрямую, например, Func<TAttribute,TResult> selector и передать att=>att.Description при вызове помощника
Показать ещё 4 комментария
Теги:
generics
.net-core

3 ответа

5
Лучший ответ

Я бы начал с этого:

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 для повторного использования.

2

Вместо этого вы можете использовать универсальную версию 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;
}
  • 1
    Не забудьте передать false в этот метод, чтобы поддерживать поведение OP.
  • 0
    Спасибо @Igor. Я собираюсь пойти с ответом StriplingWarrior. Если добавлен другой вспомогательный метод, для его выполнения потребуется меньше кода. Ваш правильный ответ тоже.
0

В С# 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();
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню