How can you create your own object in C# that can only have an already defined finite amount of values, for example like Bools as in Haskell?

Viewed 117

In Haskell you can create a new datatype for example as follows:

Player = Player1 | Player2

And further in your code you can check whether it's Player1 or Player2 by just typing Player1 and not some strange with like "Player1" that it has to be a String or check with an integer. Is something similar possible in C#? I can only think of:

class Player
{
    public int CurrentPlayer { get; private set; }
    public Player(int plyr)
    {
        CurrentPlayer = plyr;
    }
}

But now I can't check with just thisplayer = Player1 (if thisplayer is a Player). Eventually this sort of means: How can you create your own object that can only have an already defined finite amount of values, for example like Bools?

2 Answers

Use Enums:

using System;

public class Program
{
    private enum Player { PlayerOne, PlayerTwo }

    public static void Main()
    {
        Player player = Player.PlayerTwo; // Change me between Player.PlayerOne and Player.PlayerTwo to see the output change.
        switch(player)
        {
            case Player.PlayerOne:
                //this will get executed
                Console.WriteLine("Player is Player One!");
                //do stuff
                break;

            case Player.PlayerTwo:
                //this will not get executed
                Console.WriteLine("Player is Player Two!");
                //do stuff
                break;

            default:
                //this will not get executed
                break;
        }
    }
}

Output:

Player is Player Two!

See for yourself on .NET Fiddle

Enums are a way to create a somewhat-strongly-typed set of aliases for integers. However, you can always cast an integer (of whatever type you specified for your enum, by default int) to the enum type, so you have to trust the code to not do that (which might be acceptable for an internal enum type). Additionally, you can't add any additional information to the enum value itself; any time you interpret the value, you have to use a switch or similar construct.

If you want to actually have a class with properties and such, but want to restrict the number of instances of that class, you can extend the singleton pattern like this:

sealed class Player
{
    // Properties that a Player object has, an improvement over using an enum
    // which don't allow you to specify properties.
    public int Number { get; }
    public bool IsHost { get; }

    // This constructor is private, so only the code inside the Player class may create a Player object
    // (notwithstanding reflection, etc., which are outside the rules of static typing).
    private Player(int number, bool isHost)
    {
        Number = number;
        IsHost = isHost;
    }
    
    // Static properties provide singleton instances of Player for each player number.
    public static Player One { get; }   = new Player(1, true);
    public static Player Two { get; }   = new Player(2, false);
    public static Player Three { get; } = new Player(3, false);
    public static Player Four { get; }  = new Player(4, false);
}

And then use it like this:

void Main()
{
    SomethingThatTakesPlayer(Player.Three);
}

void SomethingThatTakesPlayer(Player p)
{
    Console.WriteLine($"Player #{p.Number} is the host? {p.IsHost}");
}
Related