【问题标题】:C# List that that accepts 2 or more enum types接受 2 个或更多枚举类型的 C# 列表
【发布时间】:2021-12-08 13:20:52
【问题描述】:

我将这些多个枚举用作 Id,并希望在单个列表中使用它们。

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

下面的代码可以吗?

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

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

【问题讨论】:

  • 您为什么要这样做?强类型集合是为了阻止你添加不同类型的元素。即使您将所有枚举都视为其基础整数,您如何判断它们来自哪个枚举?
  • 考虑放弃枚举并改用类多态性和接口。
  • 枚举实际上是整数。因此,您可能需要一个带有类型和值的元组。 List&lt;(Type type, int val)&gt; list = new List&lt;(Type, int)&gt;();

标签: c# types


【解决方案1】:

其他方法是通过OneOf 库使用可区分联合。

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

我发了sample


【讨论】:

  • 拆箱会很有趣...
  • @djv:一个 OneOf 实例不仅存储值本身,还存储它包含的 which 类型(即,在三种可能的情况下,索引从 0 到 2类型)。所以在装箱/拆箱过程中枚举类型信息不会丢失。
  • @Heinzi 很酷,但我的评论是关于 List&lt;object&gt; myList = new List&lt;object&gt;() 的先前编辑
  • @djv:啊,好吧,有道理。 :-)
【解决方案2】:

你可以看看ArrayList 类:

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

【讨论】:

    【解决方案3】:

    您可以实现自己的List&lt;T&gt;。这是一个简单的例子:

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

    用法:

    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
    }
    

    这只是帮助您实现自己的示例,请记住,您应该始终尝试通过指定预期的enums 来缩小可接受的参数,但您可以完全通用。

    【讨论】:

      【解决方案4】:

      正如 Andriy 所提到的,在这种情况下,使用 OneOfEither 等可区分联合可能很有用。

      如果您只是将枚举用作 ID,您还可以创建各种类并利用类型系统进行模式匹配。如果您喜欢针对特定项目进行模式匹配,这只是一种不同的模式。

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

      这将允许您编写:

      var myList = new List<EnumType>();
      myList.Add(eUnit.Warrior);
      myList.Add(eItem.Pizza);
      
      if (myList[0] is eUnit eUnit)
      {
          DoStuff();
      }
      

      如果您关心进一步的粒度,您也可以将这些静态字段转换为类,如下所示:

      internal abstract class eUnit : EnumType
      {
          private eUnit(int value, string name): base(value, name) { }
      
          // operator overloads like |, &, ^, etc...
      
          internal sealed class Villager : eUnit
          {
              private Villager(): base(0, "Villager") { }
              internal static readonly Villager _ = new Villager();
          }
      
          internal sealed class Warrior : eUnit
          {
              private Warrior(): base(1, "Warrior") { }
              internal static readonly Warrior _ = new Warrior();
          }
      
          internal sealed class Wizard : eUnit
          {
              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 : eItem
          {
              private Pizza(): base(2, "Pizza") { }
              internal static readonly Pizza _ = new Pizza();
          }
      }
      

      那么你的样本将被重写为:

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

      【讨论】:

        【解决方案5】:

        您可以尝试使用动态类型。也许这会有点矫枉过正,但这取决于你。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多