属性辅助方法的c#泛型

时间:2019-04-22 14:43:45

标签: c# generics .net-core

我找不到在.Net Core中制作此DRY的好方法。 (不要重复自己)。如何做到这一点,以免重复大部分逻辑?这是两种方法:

    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;
    }

3 个答案:

答案 0 :(得分: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方法可能更可重用。

答案 1 :(得分: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;
}

答案 2 :(得分:1)

在C#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();
    }
}