简体   繁体   中英

Extension method to convert Flags to IEnumerable<Enum> and conversely (C#)

I spent a couple hours trying to figure out a generic way of converting an Enum mask to an array of Enum values and, conversely, an array of Enum values to its Enum mask.

Writing an extension method to do so was a bit of a pain so I just wanted to share my solution in case it could help someone. I'm sure it could be improved but, here it is!

The extension method below returns a mask from a list of Enum values.

public static T ToMask<T>(this IEnumerable<T> values) where T : struct, IConvertible
{
    if (!typeof(T).IsEnum)
        throw new ArgumentException("T must be an enumerated type.");

     int builtValue = 0;
     foreach (T value in Enum.GetValues(typeof(T)))
     {
        if (values.Contains(value))
        {
            builtValue |= Convert.ToInt32(value);
        }
    }
    return (T)Enum.Parse(typeof(T), builtValue.ToString());
}

The extension method below returns a list of Enum values from a mask.

public static IEnumerable<T> ToValues<T>(this T flags) where T : struct, IConvertible
{
    if (!typeof(T).IsEnum)
        throw new ArgumentException("T must be an enumerated type.");

    int inputInt = (int)(object)(T)flags;
    foreach (T value in Enum.GetValues(typeof(T)))
    {
        int valueInt = (int)(object)(T)value;
        if (0 != (valueInt & inputInt))
        {
            yield return value;
        }
    }
}

Note that:

  1. Generic constraints in c# ( where T : ... ) cannot restrict T to an Enum
  2. These methods do not check whether the Enum has the [Flags] attribute (could not figure that out)

Usage:

[Flags]
public enum TestEnum : int
{
    None = 0,
    Plop = 1,
    Pouet = 2,
    Foo = 4,
    Bar = 8
}

To mask:

TestEnum[] enums = new[] { TestEnum.None, TestEnum.Plop, TestEnum.Foo };
TestEnum flags = enums.ToMask();

TestEnum expectedFlags = TestEnum.None | TestEnum.Plop | TestEnum.Foo;
Assert.AreEqual(expectedFlags, flags);

To values:

TestEnum flags = TestEnum.None | TestEnum.Plop | TestEnum.Foo;
TestEnum[] array = flags.ToValues().ToArray();

TestEnum[] expectedArray = new[] { TestEnum.Plop, TestEnum.Foo };
CollectionAssert.AreEqual(expectedArray, array);

我需要一个简单的解决方案来转换相反的方向(而不是必须包含扩展名),所以这是一个Linq解决方案,将你的枚举的IEnumerable转换为标记的枚举:

MyEnum e = MyEnumList.Aggregate((MyEnum)0, (a,b) => a |= b);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM