#c# #types
Вопрос:
У меня есть несколько перечислений, используемых в качестве идентификаторов, и я хочу использовать их в одном списке.
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();
...
...
Комментарии:
1. Почему ты хочешь это сделать? Строго типизированные коллекции создаются, чтобы помешать вам добавлять элементы разных типов. Даже если вы рассматриваете все перечисления как целые числа, лежащие в их основе, как бы вы определили, из какого перечисления они произошли?
2. Подумайте о том, чтобы отказаться от перечисления и вместо этого использовать полиморфизм классов и интерфейсы.
3. Перечисления на самом деле являются целыми числами. Поэтому вместо этого вам может понадобиться кортеж с типом и значением.
List<(Type type, int val)> list = new List<(Type, int)>();
Ответ №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")
);
Я сделал образец
Комментарии:
1. Будет забавно распаковать это…
2. @djv: Экземпляр oneOf хранит не только само значение, но и то, какой из типов он содержит (т. Е. Индекс от 0 до 2 в случае трех возможных типов). Таким образом, информация о типе перечисления не теряется во время бокса/распаковки.
3. @Heinzi круто, но мой комментарий был к предыдущей правке с
List<object> myList = new List<object>()
4. @djv: А, ладно, имеет смысл. 🙂
Ответ №2:
Вы могли бы попробовать использовать динамический тип. Возможно, это было бы излишне, но это зависит от вас.
Ответ №3:
Вы можете реализовать свой собственный List<T>
. Вот простой пример :
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) amp;amp; 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
}
Это всего лишь пример, который поможет вам реализовать свой собственный, помните, что вы всегда должны стараться сузить допустимые параметры, указав предполагаемые enum
параметры, хотя вы можете использовать полный универсальный.
Ответ №4:
Как уже упоминал Андрей, использование дискриминируемого союза , такого как OneOf
, или Either
, может быть полезно в этом случае.
Если вы просто используете перечисления в качестве идентификатора, вы также можете создавать различные классы и использовать систему типов для сопоставления шаблонов. Это просто другой шаблон, который вы могли бы изучить, если вам нравится сопоставление шаблонов с конкретными элементами.
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 |, amp;, ^, 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 |, amp;, ^, 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 |, amp;, ^, 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 |, amp;, ^, 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:
Вы могли бы взглянуть на 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");
}
}