如何在跳过某些值的同时遍历枚举类型

本文关键字:遍历 类型 枚举 | 更新日期: 2023-09-27 18:27:27

我问题的关键部分是跳过。我计划使用一个大约有20个元素的枚举类型。我想遍历这个集合,但每次都需要跳过一两个元素。跳过什么是事先知道的。一个类似的例子是由字母表中的所有字母组成的枚举类型,在迭代时,我希望跳过所有元音。

我应该如何以优雅/高效的方式对迭代进行编码?我应该制作一组由元音组成的单独元素吗?我没有代码可以显示,因为我只是在思考这个问题。

如何在跳过某些值的同时遍历枚举类型

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
{
    ...
}

我会制作一组由元音组成的单独元素,然后使用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
}

我可能只会使用LINQ-使用Enum.GetValues(或使用Unconstrained Melody-我编写的一个类型安全的通用枚举/委托库)来获取所有值,然后通过Where子句来表达要保留/跳过的值。

如果你只跳过特定的值,HashSet或类似的东西可能会很有用(当然,如果你只跳了一个值,那就不值得了)——如果你是根据一个条件跳过的,那么就会调用一个完整的谓词。

例如:

public static IEnumerable<T> AllBut<T>(T skipped) where T : struct
{
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
    return AllBut<T>(t => !comparer.Equals(skipped, t));
}
public static IEnumerable<T> AllBut<T>(Func<T, bool> skipPredicate) where T : struct
{
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
    return Enum.GetValues(typeof(T))
               .Cast<T>()
               .Where(t => skipPredicate(t));
}