Home > other >  C# List that that accepts 2 or more enum types
C# List that that accepts 2 or more enum types

Time:10-22

I have these multiple enums used as Ids and want to use them in a single list.

public enum eUnit
{
    Villager,
    Warrior,
    Wizard,
}
public enum eVehicle
{
    Car,
    Train,
    Helicopter,
}
public enum eItem
{
    Apple,
    Steak,
    Pizza,
}

Is something like the code below possible?

List<?enum?> myList = new List<?enum?>();
myList.Add(eUnit.Warrior);
myList.Add(eItem.Pizza);

if(myList[0].GetType() == typeof(eUnit))
    DoStuff();
...
...

CodePudding user response:

Other way is to use a discriminated union via OneOf library.

var myList = new List<OneOf<eUnit, eVehicle, eItem>>()
{
    eUnit.Warrior, eVehicle.Car
};
myList[0].Switch(
    unit => Console.WriteLine("Unit"),
    vehicle => Console.WriteLine("Vehicle"),
    item => Console.WriteLine("Item")
);

I made a sample


CodePudding user response:

You can implement your own List<T>. Here is a simple example :

public class EnumList
{
    private readonly List<Entry> _entriesList = new List<Entry>();
    public object this[int index]
    {
        get
        {
            var item = _entriesList[index];
            return Enum.Parse(item.Key, item.Value);
        }
        set
        {
            _entriesList[index] = new Entry(value.GetType(), value.ToString());
        }
    }

    private class Entry
    {
        public Type Key { get; set; }
        public string Value { get; set; }
        public Entry(Type key, string value)
        {
            Key = key;
            Value = value;
        }
    }

    public void Add<TEnum>(TEnum item) where TEnum : struct, Enum
    {
        _entriesList.Add(new Entry(typeof(TEnum), item.ToString()));
    }

    public List<TEnum> Get<TEnum>() where TEnum : struct, Enum
    {
        return _entriesList.Where(x => x.Key == typeof(TEnum)).Select(x => Enum.TryParse(x.Value, out TEnum result) ? result : default).ToList();
    }

    public bool Exists<TEnum>(TEnum item) where TEnum : struct, Enum
    {
        return _entriesList.Any(x => x.Key == typeof(TEnum) && x.Value == item.ToString());
    }
}

Usage :

var list = new EnumList();

list.Add(eUnit.Warrior);
list.Add(eItem.Pizza);
list.Add(eVehicle.Car);
list.Add(eVehicle.Helicopter);
list.Add(eUnit.Villager);
list.Add(eItem.Apple);

if((eUnit)list[0] ==  eUnit.Warrior || list.Exists(eUnit.Villager))
{
    // do stuff
}

This is just a sample to help you implement your own, remember, you should always try to narrow the acceptable params by specifying the intended enums, you could go full generic though.

CodePudding user response:

As mentioned by Andriy, using a discriminated union such as the OneOf, or Either can be useful in this case.

If you are just using the enums as an ID, you could also create various classes and utilize the type system for pattern matching. This is just a different pattern you could look into if you like pattern matching against specific items.

internal abstract class EnumType
{
    protected EnumType(int value, string name)
    {
        if(value < 0) { throw new InvalidOperationException("Value cannot be less than 0."); }
        this._value = value;
        this._name = name ?? throw new ArgumentNullException(nameof(name));
    }

    public static implicit operator int(EnumType x)
        => x?._value ?? throw new ArgumentNullException(nameof(x));

    public static implicit operator string(EnumType x)
        => x?._name ?? throw new ArgumentNullException(nameof(x));

    private readonly int _value;
    private readonly string _name;
}

internal sealed class eUnit : EnumType
{
    private eUnit(int value, string name): base(value, name) { }

    // operator overloads like |, &, ^, etc...

    internal static readonly eUnit Villager = new eUnit(0, "Villager");
    internal static readonly eUnit Warrior = new eUnit(1, "Warrior");
    internal static readonly eUnit Wizard = new eUnit(2, "Wizard");
}

internal sealed class eItem : EnumType
{
    private eItem(int value, string name): base(0, "Apple") { }

    // operator overloads like |, &, ^, etc...

    internal static readonly eItem Apple = new eItem(0, "Apple");
    internal static readonly eItem Steak = new eItem(1, "Steak");
    internal static readonly eItem Pizza = new eItem(2, "Pizza");
}

This would allow you to then write:

var myList = new List<EnumType>();
myList.Add(eUnit.Warrior);
myList.Add(eItem.Pizza);

if (myList[0] is eUnit eUnit)
{
    DoStuff();
}

If you cared about further granularity, you could turn those static fields into classes as well, along the lines of:

internal abstract class eUnit : EnumType
{
    private eUnit(int value, string name): base(value, name) { }

    // operator overloads like |, &, ^, etc...

    internal sealed class Villager
    {
        private Villager(): base(0, "Villager") { }
        internal static readonly Villager _ = new Villager();
    }

    internal sealed class Warrior
    {
        private Warrior(): base(1, "Warrior") { }
        internal static readonly Warrior _ = new Warrior();
    }

    internal sealed class Wizard
    {
        private Wizard(): base(2, "Wizard") { }
        internal static readonly Wizard _ = new Wizard();
    }
}

internal abstract class eItem : EnumType
{
    private eItem(int value, string name): base(0, "Apple") { }

    // operator overloads like |, &, ^, etc...

    //...
    internal sealed class Pizza
    {
        private Pizza(): base(2, "Pizza") { }
        internal static readonly Pizza _ = new Pizza();
    }
}

Then your sample would be rewritten as:

var myList = new List<EnumType>();
myList.Add(eUnit.Warrior._);
myList.Add(eItem.Pizza._);

var result = myList[0] switch
{
    eUnit eUnit => eUnit switch
    {
        Villager villager => DoVillagerStuff(villager),
        Warrior warrior => DoWarriorStuff(warrior),
        Wizard wizard => DoWizardStuff(wizard),
        _ => throw new InvalidOperationException("Unknonwn eItem");
    },
    eItem eItem = eItem switch
    {
        Pizza pizza => DoPizzaStuff(pizza),
        _ => throw new InvalidOperationException("Unsupported eItem")
    }
};

CodePudding user response:

You could have a look at the ArrayList class:

public enum eUnit
{
    Villager,
    Warrior,
    Wizard,
}

public enum eVehicle
{
    Car,
    Train,
    Helicopter,
}

public enum eItem
{
    Apple,
    Steak,
    Pizza,
}

void Main()
{
    var myList = new ArrayList();
    
    myList.Add(eUnit.Warrior);
    myList.Add(eItem.Pizza);
    
    for (int i = 0; i < myList.Count; i  )
    {
        var element = myList[i];
        if (element is eUnit unit)
            Console.WriteLine($"Element at index {i} is eUnit {unit}");
        else if (element is eVehicle vehicle)
            Console.WriteLine($"Element at index {i} is eVehicle {vehicle}");
        else if (element is eItem item)
            Console.WriteLine($"Element at index {i} is eItem {item}");
        else
            Console.WriteLine($"Element at index {i} is another type");
    }
}
  • Related