Как перебрать тип enum, пропуская некоторые значения? - PullRequest
14 голосов
/ 12 марта 2012

Ключевой частью моего вопроса является пропуск.Я планирую использовать тип enum, который имеет около 20 элементов.Я хочу перебрать этот набор, но нужно пропустить один или два элемента каждый раз.Что пропустить, известно заранее.Сравнимым примером является тип enum, который состоит из всех букв алфавита, и при итерации я хочу пропустить все гласные.

Как мне кодировать итерацию элегантным / эффективным способом?Должен ли я сделать отдельный набор элементов, состоящий из гласных?У меня нет кода для показа, потому что я просто думаю о проблеме.

Ответы [ 3 ]

32 голосов
/ 12 марта 2012
var query = Enum.GetValues(typeof(MyEnum))
    .Cast<MyEnum>()
    .Except(new MyEnum[] { MyEnum.A, MyEnum.E });
foreach (MyEnum item in query) {
    ...
}

Вам нужно разыграть, чтобы получить магию LINQ.Except сам по себе этого не сделает.


ОБНОВЛЕНИЕ:

У меня есть другая идея.Вы можете определить перечисление с помощью FlagsAttribute и определить регулярные значения как степени 2, чего легче всего достичь с помощью оператора побитового сдвига влево <<.Начиная с C # 7.0, вы также можете использовать двоичные литералы, такие как 0b_0000_0000_0010_0000.Затем можно объединить существующие значения для формирования новых значений.

[Flags]
enum MyEnum
{
    None = 0,
    A = 1 << 0,
    B = 1 << 1,
    C = 1 << 2,
    D = 1 << 3,
    E = 1 << 4,
    ...
    X = 1 << 23,
    Y = 1 << 24,
    Z = 1 << 25,
    Vowels = A | E | I | O | U
}

Теперь вы можете сформулировать запрос следующим образом:

IEnumerable<MyEnum> query = Enum.GetValues(typeof(MyEnum))
    .Cast<MyEnum>()
    .Where(x => (x & MyEnum.Vowels) == MyEnum.None);
foreach (MyEnum item in query) {
    ...
}

Преимущество перед первым решением состоит в том, что выможет выполнить тест с одной побитовой операцией AND.

Вы можете определить до 32 степеней двух.Если вам нужно больше, вы можете определить базовый тип перечисления как long и использовать до 64 значений флага (плюс комбинации существующих значений флага).

[Flags]
enum MyEnum : long
{
    ...
}
1 голос
/ 12 марта 2012

Я бы, вероятно, просто использовал LINQ - использовал Enum.GetValues (или использовал Unconstrained Melody - безопасную для типов универсальную библиотеку перечислений / делегатов, которую я написал), чтобы получить все значения, а затем выразить, какие значенияоставьте / пропустите с помощью предложения Where.

Если вы пропускаете только определенные значения, HashSet или что-то подобное может быть полезным (не стоит, конечно, если вы пропускаете только одно)- если вы пропускаете, основываясь на условии, то вызывается полноценный предикат.

Например:

public static IEnumerable<T> AllBut(T skipped) where T : struct
{
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
    return AllBut<T>(t => !comparer.Equals(skipped, t));
}

public static IEnumerable<T> AllBut(Func<T, bool> skipPredicate) where T : struct
{
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
    return Enum.GetValues(typeof(T))
               .Cast<T>()
               .Where(t => skipPredicate(t));
}
1 голос
/ 12 марта 2012

Я бы сделал отдельный набор элементов, состоящий из гласных, а затем взял бы разницу между двумя наборами, используя LINQ.

int[] vowels = {Letters.A, Letters.E, Letters.I, Letters.O, Letters.U};
IEnumerable<int> consonant = Enum.GetValues(typeof(Letters)).Except(vowels);
foreach (int consonant in consonants)
{
    // Do something with each consonant
}
...