简体   繁体   中英

C# Switch on enum with multiple keys for same value

I already searched on how to do a switch on the value instead of the enumeration key in C# but with no result, all the posts I've found says that we don't need to use the value, we can just use the key.

But in my case, I want to use the value because I made an enumeration with multiple keys sharing the same value, here's the code :

public enum PlayerPosition {
    North = 0,
    Top = 0, 
    South = 1,
    Bottom = 1,
    East = 2,
    Right = 2,
    West = 3,
    Left = 3
}


switch (obj.PlayerPosition)
{
    case PlayerPosition.North:
        // some code
        break;
    case PlayerPosition.South:
        // some code
        break;
    case PlayerPosition.East:
        // some code
        break;
    case PlayerPosition.West:
        // some code
        break;
    default:
        throw new ArgumentOutOfRangeException();
}

I think this is working but I don't find it really readable...

What I would like to achieve is something like this :

switch (obj.PlayerPosition)
{
    case PlayerPosition.Top:
    case PlayerPosition.North:
        // some code
        break;
    case PlayerPosition.Bottom:
    case PlayerPosition.South:
        // some code
        break;
    case PlayerPosition.Right:
    case PlayerPosition.East:
        // some code
        break;
    case PlayerPosition.Left:
    case PlayerPosition.West:
        // some code
        break;
    default:
        throw new ArgumentOutOfRangeException();
}

The example above doesn't work because it is a duplicated case label. How could I achieve that ?

EDIT : In this enumeration North/Top, South/Bottom are exactly the same, they just represent the position of the player around a table with 4 chairs. But we have old configuration files who use North/South/East/West and new configuration files who are using Top/Bottom/Right/Left.

I tested this in a dotnetfiddle : Just using the new members will work:

public enum MyEnum
{
    North  = 0, //old
    Top    = 0, //new
    South  = 1, //old
    Bottom = 1  //new
}

public static void Main()
{
    // Parse "old" from XML config (simulated)
    MyEnum test = (MyEnum)Enum.Parse(typeof(MyEnum),"North");
    //          = MyEnum.North

    switch(test)
    {
        case MyEnum.Top:
            Console.WriteLine("NORTH");
            break;
        case MyEnum.Bottom:
            Console.WriteLine("SOUTH");
            break;
        default:
            Console.WriteLine("Unsupported!");
            break;

    }
}

Will output "NORTH".

Edit

Just a word about the actual problem you are facing: Downward compatibility. What I'd do is on startup check if the configs are compatible with your current program version. If not: go through "upgrade" functions that (save a backup of the old configs and then) transform configs to the current version and save them back to disk.

You should modify the parser for the configuration files to solve this before it even becomes a problem.

Your program should only ever be ONE version at a time. The way it is now, you have 2 versions of configuration files, and you're trying to maintain 2 versions of the code at the same time.

The normal way to handle older versions of configuration files is to convert the data to the new format when they are read, or to detect them when they are first opened and convert them on disk to the new version (optionally notifying the user).

Concerning the point about not using multiple enum labels with the same value. Generally speaking, I would agree. However, there are circumstances where you could argue that it does make some sense. The following enum from the .NET System.Drawing libraries is an example:

  public enum RotateFlipType
  {
    Rotate180FlipXY = 0,
    RotateNoneFlipNone = 0,
    Rotate270FlipXY = 1,
    Rotate90FlipNone = 1,
    Rotate180FlipNone = 2,
    RotateNoneFlipXY = 2,
    Rotate270FlipNone = 3,
    Rotate90FlipXY = 3,
    Rotate180FlipY = 4,
    RotateNoneFlipX = 4,
    Rotate270FlipY = 5,
    Rotate90FlipX = 5,
    Rotate180FlipX = 6,
    RotateNoneFlipY = 6,
    Rotate270FlipX = 7,
    Rotate90FlipY = 7,
  }

This enum is used to represent the type of rotation or "flip" to perform on an image. Note that some values are the same because, eg, rotating 180 degrees is equivalent to flipping the X and Y axis.

Even in this example, there are probably other ways to represent the enum without duplicating values, but this method may be the most user-friendly.

I don't see anything inherently wrong with duplicating values, and indeed it is supported. However, you aren't able to use these duplicate enum values in structures such as switch statements.

I can't understand the reason behind having two different labels for the same enum value without having two separate enums. Firslty to answer your question you could cast the enum value to an int and then do a comparison on the int...

int value =  (int)obj.PlayerPosition

But I would probably look at removing the duplicate values and having two enums

public enum PlayerPosition {
    Top = 0, 
    Bottom = 1,
    Right = 2,
    Left = 3
}

public enum CompassPosition {
    North = 0, 
    South= 1,
    East= 2,
    West = 3
}

If you wanted to convert between the two you could just cast, eg

PlayerPosition pos = PlayerPosition.Left;
CompassPosition compass = (CompassPosition)pos;
Console.WriteLine(compass.ToString());

You cannot do this with an enum. The purpose of an enum is that each item will have a different value and can be distinguished from the others. Why would you have Top and North being the same thing when instead you can always use North and avoid the confusion of having Top in addition? I would personally just have North, South, East and West and remove the others.

However, as far as I am aware, this should work:

public enum PlayerPosition {
    North,
    Top, 
    South,
    Bottom,
    East,
    Right,
    West,
    Left
}

switch (obj.PlayerPosition)
{
    case PlayerPosition.Top:
    case PlayerPosition.North:
        // some code
        break;
    case PlayerPosition.Bottom:
    case PlayerPosition.South:
        // some code
        break;
    case PlayerPosition.Right:
    case PlayerPosition.East:
        // some code
        break;
    case PlayerPosition.Left:
    case PlayerPosition.West:
        // some code
        break;
    default:
        throw new ArgumentOutOfRangeException();
}

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