0

尝试从枚举值中获取所有可能标志的数组,例如 3 到 {1, 2} 的数组。

我有一个扩展

internal static MyEnum[] GetFlags(this MyEnum modKey)
{            
    string[] splitStr = modKey.ToString().Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries);

    MyEnum[] flags = new MyEnum[splitStr.Length];
    for (int i = 0; i < splitStr.Length; i++)
    {
        flags[i] = (MyEnum)Enum.Parse(typeof(MyEnum), splitStr[i]);
    }
    return flags;
}

......但这样做似乎有点浪费。这可以更有效地完成吗?

4

3 回答 3

4

您可以简单地将 的所有可能值过滤MyEnum到 中的值modKey

internal static MyEnum[] GetFlags(this MyEnum modKey)
{
    return Enum.GetValues(typeof(MyEnum))
        .Cast<MyEnum>()
        .Where(v => modKey.HasFlag(v))
        .ToArray();
}

编辑

根据下面的评论,在指定组合的情况下,该方法应该只返回组合,而不是所有设置的标志。

解决方案是循环遍历枚举中设置的所有标志,从最高的标志开始。在每次迭代中,我们必须在结果中添加一个标志,并将其从迭代的枚举中删除,直到它为空:

internal static MyEnum[] GetFlags(this MyEnum modKey)
{
    List<MyEnum> result = new List<MyEnum>();

    while (modKey != 0)
    {
        var highestFlag = Enum.GetValues(typeof(MyEnum))
            .Cast<MyEnum>()
            .OrderByDescending(v => v)
            .FirstOrDefault(v => modKey.HasFlag(v));

        result.Add(highestFlag);
        modKey ^= highestFlag;
    }

    return result.ToArray();
}
于 2015-11-26T21:32:52.213 回答
2

假设您的 MyEnum 具有 Flags 属性,要测试是否设置了标志(标准?)方法是在您的值和要测试的标志之间执行二进制 & :所以这样的事情应该有效:

internal static MyEnum[] GetFlags(this MyEnum modKey)
{
    List<MyEnum> flags = new List<MyEnum>();
    foreach (var flag in Enum.GetValues(typeof(MyEnum)))
    {
        if (modKey & flag == flag)
            flags.Add((MyEnum)flag);
    }
    return flags.ToArray(); 
}

如果您使用 .Net 4 或更高版本,则可以使用 HasFlag

        if (modKey.HasFlag((MyEnum)flag))
            ...
于 2015-11-26T21:25:21.763 回答
1

这两个答案都没有做(我认为)被问到的事情:从枚举值中获取基本值,而不是任何组合值。这可能有用的一个示例是,当一个枚举值必须Contains在 LINQ 中的语句中使用到不支持的 SQL 后端时HasFlag

为此,我首先创建了一个从枚举类型返回基本标志的方法:

public static class EnumUtil
{
    public static IEnumerable<TEnum> GetFlags<TEnum>()
        where TEnum : Enum
    {
        return Enum.GetValues(typeof(TEnum))
            .Cast<TEnum>()
            .Where(v =>
            {
                var x = Convert.ToInt64(v); // because enums can be Int64
                return x != 0 && (x & (x - 1)) == 0;
                // Checks whether x is a power of 2
                // Example: when x = 16, the binary values are:
                // x:         10000
                // x-1:       01111
                // x & (x-1): 00000
            });
    }
}

然后是一个从枚举值返回基本标志的方法:

    public static IEnumerable<TEnum> GetFlags<TEnum>(this TEnum enumValue)
        where TEnum : Enum
    {
        return GetFlags<TEnum>()
            .Where(ev => enumValue.HasFlag(ev));
    }

用法

采用这种枚举类型:

[Flags]
public enum WeekDay
{
    Monday = 1 << 0,
    Tuesday = 1 << 1,
    Wednesday = 1 << 2,
    Thursday = 1 << 3,
    Friday = 1 << 4,
    Saturday = 1 << 5,
    Sunday = 1 << 6,

    BusinessDay = Monday | Tuesday | Wednesday | Thursday | Friday,
    WeekendDay = Saturday | Sunday,
    All = BusinessDay | WeekendDay
}

声明(在 Linqpad 中)...

string.Join(",", EnumUtil.GetFlags<WeekDay>()).Dump();

var t = WeekDay.Thursday | WeekDay.WeekendDay;
string.Join(",", t.GetFlags()).Dump();

t = WeekDay.All;
string.Join(",", t.GetFlags()).Dump();

...返回这个:

Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
Thursday,Saturday,Sunday
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday

基本思想取自我在 Code Review 上的问题的答案

于 2021-10-28T20:32:12.520 回答