简体   繁体   中英

Is there a good way to use logical operations on System.Enum in C#?

I am playing around with learning C# by writing a small game. As a part of this, I am mapping input from devices to an enum which contains in-game actions, defined from a dictionary of button,action pairs.

For example, I have something like, schematically:

[Flags]
enum Actions { None=0, MoveUp=1, MoveDown=2, MoveLeft=4, MoveRight=8 }

Actions currentActions;

private void SetActions()
{
 currentActions = Actions.None; 
 if (UpPressed)
     currentAction |= Actions.MoveUp;
 (etc)

}

public void Update()
{
 SetActions();
 if (currentActions & Actions.MoveUp) == Actions.MoveUp)
     MoveUp();
 (etc)
}

I set the actions based on input, then I update the game's state depending upon which actions are in the list.

What I would like to do, is have a new class, "ActionManager" that contains the action-related data and methods;

public Enum currentActions;
public void SetActions();
public void UpdateActions();

and other classes, eg, a class corresponding to a menu, can add their own enums to this global Enum, so that each set of possible actions is defined inside of each class responsible for handling input, instead of being pre-defined in a global enum.

But I can't do this, because I cannot add to or remove from an enum, and I cannot inherit from System.Enum. The only way I can think of to do this involves lists of enums:

// In class ActionManager
public List<Enum> allowedActions = new List<Enum>();
public List<Enum> currentActions = new List<Enum>();

public void SetActions(Enum action)
{
 currentActions.Clear();
 foreach (Enum e in allowedActions)
     if (IsKeyPressed(e))             
         currentActions.Add(e);
}

// In, say, GameMenu class
[Flags]
enum MenuActions { None=0, MenuUp = 1, ... }

private actionManager ActionManager = new ActionManager();

public void DefineActions()
{
 foreach (MenuActions m in Enum.GetValues(typeof(MenuActions)))
     actionManager.allowedActions.Add(m);
     //also define the key corresponding to the action 
     //here, from e.g., a config file
     //and put this into a Dictionary<buttons, actions>
}

public Update()
{
 actionManager.Update();

 if (actionManager.currentActions.Contains(MenuActions.MenuUp))
 (etc)
}

But this seems stupid, I've replaced cheap logical operations with a list of Enums, which seems awkward. It also forces me to use .Contains etc, instead of arithmetic, and use reference types where value types would make more sense (and generic lists shouldn't be exposed to the outside anyway).

I realize I could also replace my List with an int, and cast all my other class's enums to ints so I can or actions together, but this doesn't have the type safety of enums.

So, my questions are: Is there a way to do this with enums? Is there a smarter way to do this kind of thing in general (either with enums or another way entirely)?

I am doing this to learn the language, so I would appreciate comments if there's a much better way to do something like this!

Yes; you can use bit operators:

Actions allowedActions = Actions.Up | Actions.Right;
allowedActions |= Actions.Down; // to show building at runtime
Actions currentActions = Actions.Right | Actions.Down; // union

and then use the various & / | / ~ to perform tests and changes:

var limitedToAllowed = currentActions & allowedActions; // intersect

// disallow "up" for some reason
allowedActions &= ~Actions.Up;

You can test for overlaps via:

// test if **any** of the currentActions is in the allowedActions
bool anyAllowed = currentActions & allowedActions != 0;
// test if **all** of the currentActions are in the allowedActions
bool allAllowed = currentActions & allowedActions == currentActions;

Your note on "which values are allowed" can usually be done either by adding a .All enum (15 in your case), or at runtime:

Actions actions = 0;
foreach(Actions action in Enum.GetValues(typeof(Actions))) {
    actions |= action;
}
// actions now has all the flags that are defined

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM