Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 936
Pedro Avatar asked Dec 05 '22 07:12

Pedro


1 Answers

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);
like image 72
Pedro Avatar answered May 10 '23 10:05

Pedro