Home > OS >  Can't use static objects in a Switch statement in Unity
Can't use static objects in a Switch statement in Unity

Time:01-09

I have Unity 2021 so it uses C# version > 7 I believe. Somehow I can not use static objects in Switch/Case statement.

  private Position getStartingPosition(Direction direction) {
    switch (direction) {
      case Direction Direction.EAST:
        return new Position(-1, height / 2);
      case Direction Direction.NORTH:
        return new Position(width / 2, height);
      case Direction Direction.WEST:
        return new Position(width, height / 2);
      case Direction Direction.SOUTH:
        return new Position(width / 2, -1);
      default:
        throw new System.Exception("Impossible");
    }
  }

and the Direction class:

public class Direction
{
    static public readonly Direction EAST = new Direction(1, 0);
    static public readonly Direction NORTH = new Direction(0, -1);
    static public readonly Direction WEST = new Direction(-1, 0);
    static public readonly Direction SOUTH = new Direction(0, 1);

...

The error I am getting is:

Grid.cs(38,31): error CS1003: Syntax error, ':' expected
Grid.cs(38,31): error CS1513: } expected
Grid.cs(38,36): error CS1003: Syntax error, ':' expected
Grid.cs(38,36): error CS1513: } expected
Grid.cs(40,31): error CS1003: Syntax error, ':' expected
Grid.cs(40,31): error CS1513: } expected

What am I doing wrong?

CodePudding user response:

The case statment should be var _ when direction.Equals(Class.VALUE) without the declaration. But also Direction is an object so one option can be use this statment:

switch (direction) {
  case var _ when direction.Equals(Direction.EAST):
    return new Position(-1, height / 2);
  case var _ when direction.Equals(Direction.NORTH):
    return new Position(width / 2, height);
  case var _ when direction.Equals(Direction.WEST):
    return new Position(width, height / 2);
  case var _ when direction.Equals(Direction.SOUTH):
    return new Position(width / 2, -1);
  default:
    throw new System.Exception("Impossible");
}

And implement the interface IEquatable<Direction> with a method similar to this:

public bool Equals(Direction otherDirection)  
{  
    return (this.x == otherDirection.x && this.y == otherDirection.y);  
}

Where x and y are values into your class used to know if two objects are equals.

CodePudding user response:

Was already answered here: Switch statement with static fields

I changed it to

    switch (direction) {
      case var _ when direction == Direction.EAST:
        return new Position(-1, height / 2);
      case var _ when direction == Direction.NORTH:
        return new Position(width / 2, height);
      case var _ when direction == Direction.WEST:
        return new Position(width, height / 2);
      case var _ when direction == Direction.SOUTH:
        return new Position(width / 2, -1);
      default:
        throw new System.Exception("Impossible");
    }

CodePudding user response:

Unity uses C# 9.0. You can only switch on constants and patterns (where a constant is a constant pattern).

We can use a positional pattern to do the test. To use it we must add a deconstructor to the class

public class Direction
{
    public Direction(int east, int south)
    {
        East = east;
        South = south;
    }

    public int East { get; }
    public int South { get; }

    public void Deconstruct(out int east, out int south)
    {
        east = East;
        south = South;
    }
}

Then we can switch like this:

switch (direction) {
    case (1, 0):
        return new Position(-1, height / 2);
    case (0, -1):
        return new Position(width / 2, height);
    case (-1, 0):
        return new Position(width, height / 2);
    case (0, 1):
        return new Position(width / 2, -1);
    default:
        throw new ArgumentException("Impossible", nameof(direction));
}

Another possible pattern is the tuple pattern not requiring a deconstructor:

switch ((direction.East, direction.South)) {
    case (1, 0):
        return new Position(-1, height / 2);
    case (0, -1):
        return new Position(width / 2, height);
    case (-1, 0):
        return new Position(width, height / 2);
    case (0, 1):
        return new Position(width / 2, -1);
    default:
        throw new ArgumentException("Impossible", nameof(direction));
}

Yet another possibility is to switch on enum constants using a constant pattern:

public enum DirectionKind
{
    None,
    East,
    North,
    West,
    South
}

We then add a property like the following one to the Direction class

public DirectionKind DirectionKind { get; }

I leave it up to you to initialize it. Then we switch like this:

switch (direction.DirectionKind) {
    case DirectionKind.East:
        return new Position(-1, height / 2);
    case DirectionKind.North:
        return new Position(width / 2, height);
    case DirectionKind.West:
        return new Position(width, height / 2);
    case DirectionKind.South:
        return new Position(width / 2, -1);
    default:
        throw new ArgumentException("Impossible", nameof(direction));
}

Finally, let's use a property pattern:

switch (direction) {
    case { East: 1, South: 0 }:
        return new Position(-1, height / 2);
    case { East: 0, South: -1 }:
        return new Position(width / 2, height);
    case { East: -1, South: 0 }:
        return new Position(width, height / 2);
    case { East: 0, South: 1 }:
        return new Position(width / 2, -1);
    default:
        throw new ArgumentException("Impossible", nameof(direction));
}

Since C# 9.0 we have the switch expression with a simplified syntax compared to the switch statement:

// Positional pattern with deconstructor
return direction switch {
    ( 1,  0) => new Position(-1, height / 2),
    ( 0, -1) => new Position(width / 2, height),
    (-1,  0) => new Position(width, height / 2),
    ( 0,  1) => new Position(width / 2, -1),
    _ => throw new ArgumentException("Impossible", nameof(direction)),
};
// Tuple pattern
return (direction.East, direction.South) switch {
    ( 1,  0) => new Position(-1, height / 2),
    ( 0, -1) => new Position(width / 2, height),
    (-1,  0) => new Position(width, height / 2),
    ( 0,  1) => new Position(width / 2, -1),
    _ => throw new ArgumentException("Impossible", nameof(direction)),
};
// Constant pattern on enum constants
return direction.DirectionKind switch {
    DirectionKind.East  => new Position(-1, height / 2),
    DirectionKind.North => new Position(width / 2, height),
    DirectionKind.West  => new Position(width, height / 2),
    DirectionKind.South => new Position(width / 2, -1),
    _ => throw new ArgumentException("Impossible", nameof(direction)),
};
// Property pattern
return direction switch { 
    { East:  1, South:  0 } => new Position(-1, height / 2), 
    { East:  0, South: -1 } => new Position(width / 2, height),
    { East: -1, South:  0 } => new Position(width, height / 2), 
    { East:  0, South:  1 } => new Position(width / 2, -1),
    _ => throw new ArgumentException("Impossible", nameof(direction)),
};

With target typed new we can write (with the positional pattern as an example):

return direction switch {
    ( 1,  0) => new (-1,        height / 2),
    ( 0, -1) => new (width / 2, height    ),
    (-1,  0) => new (width,     height / 2),
    ( 0,  1) => new (width / 2, -1        ),
    _ => throw new ArgumentException("Impossible", nameof(direction)),
};
  • Related