【问题标题】:Is there an easier way to parse an int to a generic Flags enum?有没有更简单的方法可以将 int 解析为通用 Flags 枚举?
【发布时间】:2020-02-15 17:17:04
【问题描述】:

我有一个通用函数可以将一个对象解析为一个通用枚举。

但是,我在尝试将 int 安全地解析为 [Flags] 枚举时遇到了问题。

直接使用 Enum.ToObject() 可以解析有效组合,但如果没有标志组合,则只会返回原始值。

此外,当标志组合没有明确的枚举成员时,Enum.ToName() 和 Enum.IsDefined() 不会返回有用的值。

例如:

[Flags]
public enum Color
{
   None = 0,
   Red = 1,
   Green = 2,
   Blue = 4,
}

// Returns 20
Enum.ToObject(typeof(Color), 20)

// Returns ""
Enum.ToName(typeof(Color), 3)

// Returns false
Enum.IsDefined(typeof(Color), 3)

我编写了一个我认为在技术上可行的函数,但似乎必须有更好的方法来做到这一点。

我的功能:

public static T ParseEnumerator<T>(object parseVal, T defaultVal) where T : struct, IConvertible
{
    Type ttype = typeof(T);

    if (!ttype.IsEnum)
    {
        throw new ArgumentException("T must be an enumerated type");
    }

    bool isFlag = ttype.GetCustomAttribute(typeof(FlagsAttribute)) != null;

    try
    {
        if (parseVal == null)
        {
            return defaultVal;
        }
        else if (parseVal is T)
        {
            return (T)parseVal;
        }
        else if (parseVal is string)
        {
            return (T)Enum.Parse(ttype, parseVal.ToString(), true);
        }
//**************** The section at issue **********************************/
        else if (isFlag && parseVal is int)
        {
            List<string> flagsList = new List<string>();
            int maxVal = 0;

            //Loop through each bit flag
            foreach (var val in Enum.GetValues(ttype))
            {
                if (CountBits((int)val) == 1)
                {
                    if ((int)val > maxVal)
                        maxVal = (int)val;

                    // If the current bit is set, add the flag to the result
                    if (((int)parseVal & (int)val) == (int)val)
                    {
                        string enumName = Enum.GetName(ttype, val);
                        if (!string.IsNullOrEmpty(enumName))
                            flagsList.Add(enumName);
                    } 
                }
            }

            // Is the value being parsed over the highest bitwise value?
            if ((int)parseVal >= (maxVal << 1))
                return defaultVal;
            else
                return (T)Enum.Parse(ttype, string.Join(",", flagsList));
        }
//************************************************************************/
        else
        {
            string val = Enum.GetName(ttype, parseVal);
            if (!string.IsNullOrEmpty(val))
                return (T)Enum.ToObject(ttype, parseVal);
            else
                return defaultVal;
        }
    }
    catch
    {
        return defaultVal;
    }
}

我有什么遗漏吗?还是有另一种方法可以安全地解析这些值?

感谢任何帮助,谢谢!

-MM

【问题讨论】:

    标签: c# enums enum-flags bitflags


    【解决方案1】:

    由于您的泛型函数必须首先知道 Enum 类型,因此您可以废弃该函数并改用基本类型转换。

    using System;
    
    namespace SO_58455415_enum_parsing {
    
        [Flags]
        public enum CheeseCharacteristics {
            Yellow = 1,
            Mouldy = 2,
            Soft = 4,
            Holed = 8
        }
    
        public static class Program {
            static void Main(string[] args) {
                CheeseCharacteristics cc = (CheeseCharacteristics)12;
                Console.WriteLine(cc);
            }
        }
    }
    

    如果您只想知道是否有一个可以使用枚举标志创建的值.. 这很容易,只要我们可以假设每个标志是“顺序的”(例如,在标志)。 1 和所有标志值之和之间的所有数字都可以由标志的某种组合组成。您可以简单地将标志值加在一起并将其与您的问题值进行比较。

    public static bool IsValidFlags<T>(int checkValue) where T:Enum {
        int maxFlagValue = ((int[])Enum.GetValues(typeof(T))).Sum();
    
        return (0 < checkValue) && (checkValue <= maxFlagValue);
    }
    

    为了将来参考,您可以将泛型参数限制为枚举:

    void fx<T> () where T:Enum { }
    

    【讨论】:

    • 这会遇到与 Enum.ToObject() 相同的问题。如果我们将您示例中的 12 更改为 20,结果将是“20” 我很欣赏关于将泛型限制为枚举的说明。我只是看到它以我的方式完成,所以我认为这就是你必须做的。
    • @M.McCrary:那么您希望20 的值会变成什么?例外?
    • 是的,理想情况下会抛出异常,因为它不是有效的标志组合。我知道这不是 Enums 通常的工作方式,这就是为什么对于普通 Enums,我在像往常一样投射之前检查 GetName() 值是否存在。但是对于标志枚举的情况,GetName() 和 IsDefined() 值没有帮助。
    • @M.McCrary:我更新了我的答案。您应该能够修改它以引发异常或采取任何其他您喜欢的操作。
    • 是的,这可能与我想做的事情一样好。
    猜你喜欢
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    • 2012-02-10
    • 2020-11-06
    • 1970-01-01
    • 1970-01-01
    • 2010-12-15
    • 2018-09-30
    相关资源
    最近更新 更多