C# 如何迭代具有标志的枚举的值?

C# 如何迭代具有标志的枚举的值?,c#,enums,enum-flags,C#,Enums,Enum Flags,如果我有一个包含标志枚举的变量,我能以某种方式迭代该特定变量中的单个位值吗?或者我必须使用Enum.GetValues来迭代整个枚举并检查设置了哪些?据我所知,没有任何内置方法来获取每个组件。但有一种方法可以让你得到它们: [Flags] enum Items { None = 0x0, Foo = 0x1, Bar = 0x2, Baz = 0x4, Boo = 0x6, } var value = Items.Foo | Items.Bar;

如果我有一个包含标志枚举的变量,我能以某种方式迭代该特定变量中的单个位值吗?或者我必须使用Enum.GetValues来迭代整个枚举并检查设置了哪些?据我所知,没有任何内置方法来获取每个组件。但有一种方法可以让你得到它们:

[Flags]
enum Items
{
    None = 0x0,
    Foo  = 0x1,
    Bar  = 0x2,
    Baz  = 0x4,
    Boo  = 0x6,
}

var value = Items.Foo | Items.Bar;
var values = value.ToString()
                  .Split(new[] { ", " }, StringSplitOptions.None)
                  .Select(v => (Items)Enum.Parse(typeof(Items), v));

// This method will always end up with the most applicable values
value = Items.Bar | Items.Baz;
values = value.ToString()
              .Split(new[] { ", " }, StringSplitOptions.None)
              .Select(v => (Items)Enum.Parse(typeof(Items), v)); // Boo

我调整了
Enum
在内部执行的操作,以生成字符串,而不是返回标志。您可以在reflector中查看代码,代码应该或多或少是等效的。适用于包含多个位的值的一般用例

static class EnumExtensions
{
    public static IEnumerable<Enum> GetFlags(this Enum value)
    {
        return GetFlags(value, Enum.GetValues(value.GetType()).Cast<Enum>().ToArray());
    }

    public static IEnumerable<Enum> GetIndividualFlags(this Enum value)
    {
        return GetFlags(value, GetFlagValues(value.GetType()).ToArray());
    }

    private static IEnumerable<Enum> GetFlags(Enum value, Enum[] values)
    {
        ulong bits = Convert.ToUInt64(value);
        List<Enum> results = new List<Enum>();
        for (int i = values.Length - 1; i >= 0; i--)
        {
            ulong mask = Convert.ToUInt64(values[i]);
            if (i == 0 && mask == 0L)
                break;
            if ((bits & mask) == mask)
            {
                results.Add(values[i]);
                bits -= mask;
            }
        }
        if (bits != 0L)
            return Enumerable.Empty<Enum>();
        if (Convert.ToUInt64(value) != 0L)
            return results.Reverse<Enum>();
        if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L)
            return values.Take(1);
        return Enumerable.Empty<Enum>();
    }

    private static IEnumerable<Enum> GetFlagValues(Type enumType)
    {
        ulong flag = 0x1;
        foreach (var value in Enum.GetValues(enumType).Cast<Enum>())
        {
            ulong bits = Convert.ToUInt64(value);
            if (bits == 0L)
                //yield return value;
                continue; // skip the zero value
            while (flag < bits) flag <<= 1;
            if (flag == bits)
                yield return value;
        }
    }
}

据我所知,没有任何内置方法来获取每个组件。但有一种方法可以让你得到它们:

[Flags]
enum Items
{
    None = 0x0,
    Foo  = 0x1,
    Bar  = 0x2,
    Baz  = 0x4,
    Boo  = 0x6,
}

var value = Items.Foo | Items.Bar;
var values = value.ToString()
                  .Split(new[] { ", " }, StringSplitOptions.None)
                  .Select(v => (Items)Enum.Parse(typeof(Items), v));

// This method will always end up with the most applicable values
value = Items.Bar | Items.Baz;
values = value.ToString()
              .Split(new[] { ", " }, StringSplitOptions.None)
              .Select(v => (Items)Enum.Parse(typeof(Items), v)); // Boo

我调整了
Enum
在内部执行的操作,以生成字符串,而不是返回标志。您可以在reflector中查看代码,代码应该或多或少是等效的。适用于包含多个位的值的一般用例

static class EnumExtensions
{
    public static IEnumerable<Enum> GetFlags(this Enum value)
    {
        return GetFlags(value, Enum.GetValues(value.GetType()).Cast<Enum>().ToArray());
    }

    public static IEnumerable<Enum> GetIndividualFlags(this Enum value)
    {
        return GetFlags(value, GetFlagValues(value.GetType()).ToArray());
    }

    private static IEnumerable<Enum> GetFlags(Enum value, Enum[] values)
    {
        ulong bits = Convert.ToUInt64(value);
        List<Enum> results = new List<Enum>();
        for (int i = values.Length - 1; i >= 0; i--)
        {
            ulong mask = Convert.ToUInt64(values[i]);
            if (i == 0 && mask == 0L)
                break;
            if ((bits & mask) == mask)
            {
                results.Add(values[i]);
                bits -= mask;
            }
        }
        if (bits != 0L)
            return Enumerable.Empty<Enum>();
        if (Convert.ToUInt64(value) != 0L)
            return results.Reverse<Enum>();
        if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L)
            return values.Take(1);
        return Enumerable.Empty<Enum>();
    }

    private static IEnumerable<Enum> GetFlagValues(Type enumType)
    {
        ulong flag = 0x1;
        foreach (var value in Enum.GetValues(enumType).Cast<Enum>())
        {
            ulong bits = Convert.ToUInt64(value);
            if (bits == 0L)
                //yield return value;
                continue; // skip the zero value
            while (flag < bits) flag <<= 1;
            if (flag == bits)
                yield return value;
        }
    }
}

您不需要迭代所有值。只需检查您的特定标志,如下所示:

if((myVar & FlagsEnum.Flag1) == FlagsEnum.Flag1) 
{
   //do something...
}
或者(如评论中所述),您可以检查是否使用它,如:

if(myVar.HasFlag(FlagsEnum.Flag1))
{
   //do something...
}

您不需要迭代所有值。只需检查您的特定标志,如下所示:

if((myVar & FlagsEnum.Flag1) == FlagsEnum.Flag1) 
{
   //do something...
}
或者(如评论中所述),您可以检查是否使用它,如:

if(myVar.HasFlag(FlagsEnum.Flag1))
{
   //do something...
}

您可以使用枚举中的迭代器。从MSDN代码开始:

public class DaysOfTheWeek : System.Collections.IEnumerable
{
    int[] dayflag = { 1, 2, 4, 8, 16, 32, 64 };
    string[] days = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
    public string value { get; set; }

    public System.Collections.IEnumerator GetEnumerator()
    {
        for (int i = 0; i < days.Length; i++)
        {
            if value >> i & 1 == dayflag[i] {
                yield return days[i];
            }
        }
    }
}
公共类DaysOfTheWeek:System.Collections.IEnumerable
{
int[]dayflag={1,2,4,8,16,32,64};
字符串[]天={“周一”、“周二”、“周三”、“周四”、“周五”、“周六”、“周日”};
公共字符串值{get;set;}
public System.Collections.IEnumerator GetEnumerator()
{
for(int i=0;i>i&1==dayflag[i]{
收益回报天数[i];
}
}
}
}

它没有经过测试,所以如果我犯了错误,请随时给我打电话。(显然它不是可重入的。)您必须事先赋值,或者将其分解为另一个使用enum.dayflag和enum.days的函数。您可能可以使用大纲去某个地方。

您可以使用枚举中的迭代器。从MSDN代码开始:

public class DaysOfTheWeek : System.Collections.IEnumerable
{
    int[] dayflag = { 1, 2, 4, 8, 16, 32, 64 };
    string[] days = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
    public string value { get; set; }

    public System.Collections.IEnumerator GetEnumerator()
    {
        for (int i = 0; i < days.Length; i++)
        {
            if value >> i & 1 == dayflag[i] {
                yield return days[i];
            }
        }
    }
}
static IEnumerable<Enum> GetFlags(Enum input)
{
    foreach (Enum value in Enum.GetValues(input.GetType()))
        if (input.HasFlag(value))
            yield return value;
}
公共类DaysOfTheWeek:System.Collections.IEnumerable
{
int[]dayflag={1,2,4,8,16,32,64};
字符串[]天={“周一”、“周二”、“周三”、“周四”、“周五”、“周六”、“周日”};
公共字符串值{get;set;}
public System.Collections.IEnumerator GetEnumerator()
{
for(int i=0;i>i&1==dayflag[i]{
收益回报天数[i];
}
}
}
}
它没有经过测试,所以如果我犯了错误,请随时给我打电话。(显然它不是可重入的。)您必须事先赋值,或者将其分解为另一个使用enum.dayflag和enum.days的函数。您可能可以使用该大纲去某个地方。

静态IEnumerable GetFlags(枚举输入)
static IEnumerable<Enum> GetFlags(Enum input)
{
    foreach (Enum value in Enum.GetValues(input.GetType()))
        if (input.HasFlag(value))
            yield return value;
}
{ foreach(Enum.GetValues(input.GetType())中的枚举值) if(输入.HasFlag(值)) 收益回报值; }
静态IEnumerable GetFlags(枚举输入)
{
foreach(Enum.GetValues(input.GetType())中的枚举值)
if(输入.HasFlag(值))
收益回报值;
}

对上述答案不满意,尽管它们只是开始

在这里拼凑了一些不同的来源后:


我创造了这个,让我知道你的想法。
参数:
bool checkZero
:告诉它允许
0
作为标志值。默认情况下,
input=0
返回空值。
bool checkFlags
:告诉它检查
Enum
是否使用
[Flags]
属性修饰。
顺便说一句,我现在没有时间找出
checkcombinats=false
alg,这将迫使它忽略任何作为位组合的枚举值

    public static IEnumerable<TEnum> GetFlags<TEnum>(this TEnum input, bool checkZero = false, bool checkFlags = true, bool checkCombinators = true)
    {
        Type enumType = typeof(TEnum);
        if (!enumType.IsEnum)
            yield break;

        ulong setBits = Convert.ToUInt64(input);
        // if no flags are set, return empty
        if (!checkZero && (0 == setBits))
            yield break;

        // if it's not a flag enum, return empty
        if (checkFlags && !input.GetType().IsDefined(typeof(FlagsAttribute), false))
            yield break;

        if (checkCombinators)
        {
            // check each enum value mask if it is in input bits
            foreach (TEnum value in Enum<TEnum>.GetValues())
            {
                ulong valMask = Convert.ToUInt64(value);

                if ((setBits & valMask) == valMask)
                    yield return value;
            }
        }
        else
        {
            // check each enum value mask if it is in input bits
            foreach (TEnum value in Enum <TEnum>.GetValues())
            {
                ulong valMask = Convert.ToUInt64(value);

                if ((setBits & valMask) == valMask)
                    yield return value;
            }
        }

    }
最后,这里是一个使用它的示例:

    private List<CountType> GetCountTypes(CountType countTypes)
    {
        List<CountType> cts = new List<CountType>();

        foreach (var ct in countTypes.GetFlags())
            cts.Add(ct);

        return cts;
    }
私有列表GetCountTypes(CountType countTypes)
{
List cts=新列表();
foreach(countTypes.GetFlags()中的变量ct)
cts.Add(ct);
返回cts;
}

对上述答案不满意,尽管它们只是开始

在这里拼凑了一些不同的来源后:


我创造了这个,让我知道你的想法。
参数:
bool checkZero
:告诉它允许
0
作为标志值。默认情况下,
input=0
返回空值。
bool checkFlags
:告诉它检查
Enum
是否使用
[Flags]
属性修饰。
顺便说一句,我现在没有时间找出
checkcombinats=false
alg,这将迫使它忽略任何作为位组合的枚举值

    public static IEnumerable<TEnum> GetFlags<TEnum>(this TEnum input, bool checkZero = false, bool checkFlags = true, bool checkCombinators = true)
    {
        Type enumType = typeof(TEnum);
        if (!enumType.IsEnum)
            yield break;

        ulong setBits = Convert.ToUInt64(input);
        // if no flags are set, return empty
        if (!checkZero && (0 == setBits))
            yield break;

        // if it's not a flag enum, return empty
        if (checkFlags && !input.GetType().IsDefined(typeof(FlagsAttribute), false))
            yield break;

        if (checkCombinators)
        {
            // check each enum value mask if it is in input bits
            foreach (TEnum value in Enum<TEnum>.GetValues())
            {
                ulong valMask = Convert.ToUInt64(value);

                if ((setBits & valMask) == valMask)
                    yield return value;
            }
        }
        else
        {
            // check each enum value mask if it is in input bits
            foreach (TEnum value in Enum <TEnum>.GetValues())
            {
                ulong valMask = Convert.ToUInt64(value);

                if ((setBits & valMask) == valMask)
                    yield return value;
            }
        }

    }
最后,这里是一个使用它的示例:

    private List<CountType> GetCountTypes(CountType countTypes)
    {
        List<CountType> cts = new List<CountType>();

        foreach (var ct in countTypes.GetFlags())
            cts.Add(ct);

        return cts;
    }
私有列表GetCountTypes(CountType countTypes)
{
List cts=新列表();
foreach(countTypes.GetFlags()中的变量ct)
cts.Add(ct);
返回cts;
}

我所做的是更改我的方法,而不是将方法的输入参数键入
enum
类型,而是将其键入
enum
类型的数组(
MyEnum[]myEnums
),这样我只需在循环中使用switch语句迭代数组。

我所做的是更改我的方法,我没有将方法的输入参数输入为
enum
类型,而是将其输入为
enum
类型的数组(
MyEnum[]myEnums
),这样我就在循环中使用switch语句迭代数组。

基于Greg的上述答案,这还考虑了枚举中的值为0的情况,例如None=0。在这种情况下,它不应该迭代该值

public static IEnumerable<Enum> ToEnumerable(this Enum input)
{
    foreach (Enum value in Enum.GetValues(input.GetType()))
        if (input.HasFlag(value) && Convert.ToInt64(value) != 0)
            yield return value;
}
公共静态IEnumerable到numerable
public static string GetEnumString(MyEnum inEnumValue)
{
    StringBuilder sb = new StringBuilder();

    foreach (MyEnum e in Enum.GetValues(typeof(MyEnum )))
    {
        if ((e & inEnumValue) != 0)
        {
           sb.Append(e.ToString());
           sb.Append(", ");
        }
    }

   return sb.ToString().Trim().TrimEnd(',');
}
public static IEnumerable<T> GetUniqueFlags<T>(this T flags)
    where T : Enum    // New constraint for C# 7.3
{
    foreach (Enum value in Enum.GetValues(flags.GetType()))
        if (flags.HasFlag(value))
            yield return (T)value;
}
[Flags]
enum Food
{
  None=0
  Bread=1,
  Pasta=2,
  Apples=4,
  Banana=8,
  WithGluten=Bread|Pasta,
  Fruits = Apples | Banana,
}
public static class EnumExtensions
{
    public static T[] GetFlags<T>(this T flagsEnumValue) where T : Enum
    {
        return Enum
            .GetValues(typeof(T))
            .Cast<T>()
            .Where(e => flagsEnumValue.HasFlag(e))
            .ToArray();
    }
}
public static IEnumerable<T> GetUniqueFlags<T>(this T value)
    where T : Enum
{
    var valueLong = Convert.ToUInt64(value, CultureInfo.InvariantCulture);
    foreach (var enumValue in value.GetType().GetEnumValues())
    {
        if (
            enumValue is T flag // cast enumValue to T
            && Convert.ToUInt64(flag, CultureInfo.InvariantCulture) is var bitValue // convert flag to ulong
            && (bitValue & (bitValue - 1)) == 0 // is this a single-bit value?
            && (valueLong & bitValue) != 0 // is the bit set?
           )
        {
            yield return flag;
        }
    }
}
public static class FlagEnumExtensions
{
    public static IEnumerable<T> GetFlags<T>(this T en) where T : struct, Enum
    {
        return Enum.GetValues<T>().Where(member => en.HasFlag(member));
    }
}