检查枚举值是否处于所需状态之一。

21
如果我有枚举类型
    [Flags]
    public enum GameFlow
    {
        Normal = 1,
        NormalNoMove = 2,
        Paused = 4,
        Battle = 8
    }

是否可以通过一次检查来确定枚举是否处于期望状态之一?例如,如果我想检查枚举是否为Normal或NormalNoMove,我是否总是需要像这样编写代码?

if(Flow == GameFlow.Normal || Flow == GameFlow.NormalNoMove)

如果只有两个值,这并不是一个大问题,但如果有更多的枚举状态,只在一个地方更改它会很好。有没有可能创建一个枚举别名,如果枚举值是Normal或NormalNoMove,则返回true?还是我需要编写某种辅助方法来实现这一点(扩展方法?)


我猜测Flow是GameFlow类型的? - Francis.Beauchamp
是的,它是GameFlow类型的。 - kasztelan
6个回答

27

也许它会有用

public static class EnumExtensions
{
    public static bool IsOneOf(this Enum enumeration, params Enum[] enums)
    {
        return enums.Contains(enumeration);
    }
}

// usage:
if(Flow.IsOneOf(GameFlow.Normal, GameFlow.NormalNoMove))

5
添加类型检查后:public static bool IsOneOf<T>(this T enumeration, params T[] enums) where T : Enum - chrisjshields

18

按位逻辑应该可以像这样在标志枚举上工作。

if((Flow & (GameFlow.Normal | GameFlow.NormalNoMove)) > 0)

如我在这里提到的那样,还可以创建组合其他值的枚举值。

因此,在您的情况下:

[Flags]
public enum GameFlow
{
    Normal = 1,
    NormalNoMove = 2,
    Paused = 4,
    Battle = 8,
    AnyNormal = Normal | NormalNoMove
}

bool IsNormal(GameFlow flow)
{
    return (flow & GameFlow.AnyNormal) > 0;
}

还有一个 LINQPad 测试:

void Main()
{
    IsNormal(GameFlow.Normal).Dump();// True
    IsNormal(GameFlow.NormalNoMove).Dump();// True
    IsNormal(GameFlow.Paused).Dump();// False
    IsNormal(GameFlow.Battle).Dump();// False

    IsNormal(GameFlow.Normal | GameFlow.Paused).Dump();// True
    IsNormal(GameFlow.NormalNoMove  | GameFlow.Battle).Dump();// True
    IsNormal(GameFlow.Paused | GameFlow.Battle).Dump();// False
    IsNormal(GameFlow.Battle | GameFlow.Normal).Dump();// True

}

根据您的评论,我想知道是否您也应该修改这里的位标志。听起来“Normal”是您想要检查的状态,“NormalNoMove”基于此构建。也许您的枚举应该更像这样:

[Flags]
public enum GameFlow
{
    Normal = 1,
    NormalNoMove = Normal | 2,
    Paused = 4,
    Battle = 8
}

这样,您可以检查 flow & GameFlow.Normal > 0 是否为真,以确定您目前处于其中任一普通状态:NormalNoMove 只是在某种程度上“扩展”了Normal


是的,我知道,但是我仍然必须在每个进行条件检查的地方显式使用Normal和NormalNoMove。我希望有一种方法可以创建另一个枚举状态,例如NormalState,如果值为Normal或NormalNoMove,则在条件检查中返回true。 - kasztelan
@kasztelan:我扩展了我的答案以回应你的评论。 - StriplingWarrior
谢谢!(flow&GameFlow.AnyNormal)> 0;是我正在寻找的答案。这样,如果我想要通过第三个状态扩展AnyNormal,我只需要更改枚举中的声明即可。完美! - kasztelan
@kasztelan:如果“NormalNoMove”只是“Normal”状态的扩展,您可能需要以不同的方式声明它。请参阅我的更新。 - StriplingWarrior
在这种情况下,你不能简单地使用“<= NormalNoMove”吗? - ChrisFox
3
@ChrisFox:你可以这样做,但要理解为什么它有效,你必须了解枚举值的具体信息。新开发人员需要查看枚举的实现细节才能理解您代码的意图,而任何了解标志枚举的人都会读取 (flow & GameFlow.Normal) > 0 并说“哦,他们想知道是否激活了 GameFlow.Normal 标志”。 - StriplingWarrior

14

根据C# 9的规定,我会使用模式匹配来编写这样的表达式。在这种情况下。

if(Flow is GameFlow.Normal or GameFlow.NormalNoMove) 
{
   // your code...
}

4

根据@StringlingWarrior答案下的评论,你可以创建一个扩展方法来简化你的代码:

public static class GameFlowExtensions
{
    public static bool IsNormal(this GameFlow flow)
    {
        return (flow & (GameFlow.Normal | GameFlow.NormalNoMove)) > 0;
    }
}

// usage:
if (Flow.IsNormal()) 

谢谢,但幸运的是我不必创建扩展方法,(flow & GameFlow.AnyNormal) > 0; 部分正是我正在寻找的。 - kasztelan

1
您可以将一个变量设置为GameFlow.NormalGameFlow.NormalNoMove,然后将您拥有的值与其进行比较,如下所示:
GameFlow NormalOrNormalNoMove = GameFlow.Normal | GameFlow.NormalNoMove;
...
if ((Flow & NormalOrNormalNoMove) > 0)
{
    // Your code
}

0
var requiredFlows = new [] {
    GameFlow.NormalNoMove,
    GameFlow.Normal,
    // Other values of the GameFlow enum.
}.ToHashSet();

if (requiredFlows.Contains(currentFlow))
{
    // Your code.
}

理想情况下,requiredFlows 应该是一个 private static readonly 字段。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接