-1

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?

nurp
  • 1,239
  • 2
  • 14
  • 23

3 Answers3

2

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.

J.F.
  • 13,927
  • 9
  • 27
  • 65
1

You can only switch on constants and patterns (where a constant is considered as a constant pattern). Unity uses C# 9.0 providing powerful pattern matching expressions. Since C# 9.0 we have the switch expression with a simplified syntax compared to the switch statement. I therefore suggest using switch expressions in conjunction with pattern matching.

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;
    }

    // You have not shown your whole class.
    // I assume that it has two properties for the main directions.
    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:

// 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)),
};

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

// 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)),
};

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:

// 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)),
};

The property pattern does not require a sepcial infrastructure:

// 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)),
};

There is also a type pattern. It can be used if we declare the directions as a class hierarchy.

public abstract class Direction
{
    public abstract int East { get; }
    public abstract int South { get; }
}

public class EastDirection : Direction
{
    private EastDirection() { } // Hide constructor to implement a singleton.

    public static readonly Direction Instance = new EastDirection();

    public override int East => 1;
    public override int South => 0;
}

public class NorthDirection : Direction
{
    private NorthDirection() { }

    public static readonly Direction Instance = new NorthDirection();

    public override int East => 0;
    public override int South => -1;
}

public class WestDirection : Direction
{
    private WestDirection() { }

    public static readonly Direction Instance = new WestDirection();

    public override int East => -1;
    public override int South => 0;
}

public class SouthDirection : Direction
{
    private SouthDirection() { }

    public static readonly Direction Instance = new SouthDirection();

    public override int East => 0;
    public override int South => 1;
}
// Type pattern
return direction switch {
    EastDirection => new Position(-1, height / 2),
    NorthDirection => new Position(width / 2, height),
    WestDirection => new Position(width, height / 2),
    SouthDirection => new Position(width / 2, -1),
    _ => throw new ArgumentException("Impossible", nameof(direction)),
};

Note: I used the singleton pattern here as a suggestion. I is not required for the switch expression.


This class hierarchy gives us even a way to eliminate the switch expression altogether by adding an abstract GetPosition method to Direction.

public abstract Position GetPosition(int width, int height);

As an example WestDirection would implement it like this:

public override Position GetPosition(int width, int height)
{
    return new Position(width, height / 2);
}

Given a direction you can get a position like this

Direction direction = ...;
Position position = direction.GetPosition(width, height); 

This is the true OOP way to solve the problem.


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)),
};
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
0

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");
    }

nurp
  • 1,239
  • 2
  • 14
  • 23
  • 1
    For this to work properly, you need to make sure that there is only one instance for each direction, as you are comparing using reference equality. You can accomplish this by making the constructor private. – Olivier Jacot-Descombes Jan 08 '23 at 16:12