C# Преобразование установленных флагов в переменной типа перечисления флагов в массив целых чисел

Я придумал этот фрагмент кода, который преобразует установленные флаги в переменную типа Перечисление флагов и возвращает установленные флаги как целые числа. Я хотел бы знать, является ли это лучшим подходом.

Пример перечисления:

[Flags]
enum Status {
  None = 0x0,
  Active = 0x1,
  Inactive = 0x2,
  Canceled = 0x4,
  Suspended = 0x8
}

Метод расширения, который преобразует установленные флаги в массив int, который я придумал:

public static class Extensions
{
    public static int[] ToIntArray(this System.Enum o) 
    {
        return o.ToString()
            .Split(new string[] { ", " }, StringSplitOptions.None)
            .Select(i => (int)Enum.Parse(o.GetType(), i))
            .ToArray();
    }
}

Вот как я его использую:

Status filterStatus = Status.Suspended | Status.Canceled;

int[] filterFlags = filterStatus.toIntArray();

foreach (int flag in filterFlags) {
   Console.WriteLine("{0}\n", flag);
}

Он выведет:

4
8

Как видите, для этого я делаю следующее:

  1. Преобразование переменной в строку. Выводит что-то вроде: Suspended, Canceled
  2. Разделение этой строки на массив строк: { "Приостановлено", "Отменено" }
  3. Преобразование этой строки в значение перечисления с помощью Enum.Parse.
  4. Приведение значения к целому числу.
  5. Преобразование IEnumerable в int[].

Это работает, но я просто не думаю, что это лучший подход. Любые предложения по улучшению этого фрагмента кода?


person Cameri    schedule 08.07.2010    source источник
comment
Не все типы enum основаны на int как на базовом типе, поэтому приведение может завершиться ошибкой.   -  person Ben Voigt    schedule 09.07.2010
comment
@Ben - Это хороший момент, и это повлияет на мое решение в ответе. Лучшая идея, вероятно, состоит в том, чтобы понять, что это не общее решение, а конкретное, которое можно применить, когда вы знаете что-то об используемом перечислении. Вы можете сойти с ума с помощью универсального базового типа, но я думаю, что в большинстве случаев это было бы излишним.   -  person Steve Mitcham    schedule 09.07.2010
comment
Это действительно не общее решение, и оно специфично только для перечислений с базовым типом System.Int32. Кроме того, все значения в перечислении независимы, поэтому решение Стива Митчема работает, сохраняя при этом LINQ-подобность.   -  person Cameri    schedule 09.07.2010


Ответы (3)


Чтобы это было похоже на linq

var flags = Enum.GetValues(typeof(Status))
                .Cast<int>()
                .Where(f=> f & o == f)
                .ToList();

Одна проблема с этим подходом заключается в том, что он будет включать совокупные значения перечисления. Например:

[Flags]
public enum Status
{
    None = 0,
    One = 1,
    Two = 2,
    All = One | Two,
}

var flags = Enum.GetValues(typeof(Status))
                .Cast<int>()
                .Where(f=> f & o == f)
                .ToList();

Здесь flags будет 1, 2, 3, а не только 1, 2.

person Steve Mitcham    schedule 08.07.2010
comment
Должно быть Where(f => f&o == f) - person Ben Voigt; 09.07.2010
comment
@LukeH: результат f & o не ограничивается f и 0. Рассмотрим, например, перечисление System.Windows.Forms.Keys. - person Ben Voigt; 09.07.2010
comment
@Бен: Ты прав. И я также согласен с вашим мнением о слепках с Enum по ulong. Я удалил свои вводящие в заблуждение комментарии. (Это было очень поздно, когда я отправил сообщение, и я, очевидно, не соображал!) - person LukeH; 09.07.2010

Просто приведите к типу int, а затем распечатайте отдельные биты.

int x = (int)o
List<int> flags = new List<int>();
for(int i = 1; i < (1 << 30); i <<= 1)
    if(x & i != 0)
        flags.Add(i);
return flags;
person Anon.    schedule 08.07.2010