改进 EnumHelper 功能并新增全面单元测试
石头 authored at 2025-09-27 10:16:00
4.46 KiB
X
using System.ComponentModel;
using System.Reflection;

namespace NewLife;

/// <summary>枚举类型助手类</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class EnumHelper
{
    /// <summary>枚举变量是否包含指定标识</summary>
    /// <param name="value">枚举变量</param>
    /// <param name="flag">要判断的标识</param>
    /// <returns>如果枚举变量包含指定标识则返回 true,否则返回 false</returns>
    /// <exception cref="ArgumentException">当两个枚举类型不匹配时</exception>
    public static Boolean Has(this Enum value, Enum flag)
    {
        if (value.GetType() != flag.GetType()) throw new ArgumentException("Enumeration identification judgment must be of the same type", nameof(flag));

        var num = Convert.ToUInt64(flag);
        // 特殊处理:当flag为0时,只有当value也为0时才返回true
        if (num == 0) return Convert.ToUInt64(value) == 0;

        return (Convert.ToUInt64(value) & num) == num;
    }

    /// <summary>设置枚举标识位</summary>
    /// <typeparam name="T">枚举类型</typeparam>
    /// <param name="source">源枚举值</param>
    /// <param name="flag">要设置的标识</param>
    /// <param name="value">是否设置该标识</param>
    /// <returns>设置后的枚举值</returns>
    /// <exception cref="ArgumentException">当枚举类型不匹配时</exception>
    public static T Set<T>(this Enum source, T flag, Boolean value)
    {
        if (source is not T) throw new ArgumentException("Enumeration identification judgment must be of the same type", nameof(source));

        var s = Convert.ToUInt64(source);
        var f = Convert.ToUInt64(flag);

        if (value)
            s |= f;
        else
            s &= ~f;

        return (T)Enum.ToObject(typeof(T), s);
    }

    /// <summary>获取枚举字段的描述</summary>
    /// <param name="value">枚举值</param>
    /// <returns>如果存在 DescriptionAttribute 则返回其描述,否则返回 null</returns>
    public static String? GetDescription(this Enum value)
    {
        if (value == null) return null;

        var type = value.GetType();
        var field = type.GetField(value.ToString(), BindingFlags.Public | BindingFlags.Static);

        // 云飞扬 2017-07-06 传入的枚举值可能并不存在,需要判断是否为 null
        if (field == null) return null;

        var description = field.GetCustomAttribute<DescriptionAttribute>(false);
        return description?.Description;
    }

    /// <summary>获取枚举类型的所有字段描述</summary>
    /// <typeparam name="TEnum">枚举类型</typeparam>
    /// <returns>包含枚举值与其描述的字典</returns>
    public static Dictionary<TEnum, String> GetDescriptions<TEnum>() where TEnum : notnull
    {
        var result = new Dictionary<TEnum, String>();
        var descriptions = GetDescriptions(typeof(TEnum));

        foreach (var kvp in descriptions)
        {
            result.Add((TEnum)Enum.ToObject(typeof(TEnum), kvp.Key), kvp.Value);
        }

        return result;
    }

    /// <summary>获取枚举类型的所有字段描述</summary>
    /// <param name="enumType">枚举类型</param>
    /// <returns>包含枚举值(Int32)与其描述的字典</returns>
    public static Dictionary<Int32, String> GetDescriptions(Type enumType)
    {
        var result = new Dictionary<Int32, String>();

        // 检查是否为枚举类型
        if (!enumType.IsEnum) return result;

        var fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static);

        foreach (var field in fields)
        {
            if (!field.IsStatic) continue;

            var enumValue = Convert.ToInt32(field.GetValue(null));

            // 优先使用 DisplayNameAttribute
            var displayName = field.GetCustomAttribute<DisplayNameAttribute>(false);
            var description = displayName?.DisplayName;

            // 其次使用 DescriptionAttribute
            var descriptionAttr = field.GetCustomAttribute<DescriptionAttribute>(false);
            if (description.IsNullOrEmpty()) description = descriptionAttr?.Description;

            // 有些枚举可能不同名称有相同的值,使用索引器避免重复键异常
            if (description.IsNullOrEmpty()) description = field.Name;
            result[enumValue] = description;
        }

        return result;
    }
}