Мне нравятся преимущества перечислений Java, и я хотел попробовать их смоделировать. Я просмотрел много постов здесь и не смог найти простой способ сделать это, поэтому я придумал следующее. Я не профессионал, поэтому, пожалуйста, прости меня за любые глупые ошибки.
Это, как я полагаю, обеспечивает почти всю функциональность перечисления Java (я не писал метод клонирования).
Кроме того, я прошу прощения за длину этого сообщения.
Это абстрактный класс:
#region + Using Directives
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using TestEnum;
#endregion
namespace TestEnum
{
public abstract class ACsEnum<T, U, V> :
IComparable<ACsEnum<T, U, V>>
where T : ACsEnum<T, U, V>
{
static ACsEnum()
{
// the static constructor causes
// early creation of the object
Count = 0;
}
// constructor
protected ACsEnum(Enum m, V v)
{
Enum = m;
Value = v;
Ordinal = Count++;
}
#region Admin Private fields
// the list of members
protected static readonly List<T> members = new List<T>();
#endregion
#region Admin Public Properties
// the enum associated with this class
public Enum Enum { get; }
// number of members
public static int Count { get; private set; }
// ordinal number of this member (zero based)
public int Ordinal { get; }
// a name of this member
public string Name => ToString();
// the value attached to the enum
public U Amount => (U) (IConvertible) Enum;
// the value of this member - this value is
// returned from the implicit conversion
public V Value { get; }
#endregion
#region Admin Operator
public static implicit operator V (ACsEnum<T, U, V> m)
{
return m.Value;
}
#endregion
#region Admin Functions
// compare
public int CompareTo(ACsEnum<T, U, V> other)
{
if (other.GetType() != typeof(T)) { return -1; }
return Ordinal.CompareTo(other.Ordinal);
}
// determine if the name provided is a member
public static bool IsMember(string name, bool caseSensitive)
{
return Find(name, caseSensitive) != null;
}
// finds and returns a member
public static T Find (string name, bool caseSensitive = false)
{
if (caseSensitive)
{
return members.Find(s => s.ToString().Equals(name));
}
else
{
return members.Find(s => s.ToString().ToLower().Equals(name.ToLower()));
}
}
// allow enumeration over the members
public static IEnumerable Members()
{
foreach (T m in members)
{
yield return m;
}
}
// get the members as an array
public static T[] Values()
{
return members.ToArray();
}
// same as Find but throws an exception
public static T ValueOf(string name)
{
T m = Find(name, true);
if (m != null) return m;
throw new InvalidEnumArgumentException();
}
#endregion
#region Admin Overrides
public override bool Equals(object obj)
{
if (obj != null && obj.GetType() != typeof(T)) return false;
return ((T) obj).Ordinal == Ordinal ;
}
#endregion
}
}
Это метод расширения, чтобы помочь с enum:
namespace TestEnum
{
// special enum extension that gets the value of an enum
//
public static class EnumEx
{
public static dynamic Value(this Enum e)
{
switch (e.GetTypeCode())
{
case TypeCode.Byte:
{
return (byte) (IConvertible) e;
}
case TypeCode.Int16:
{
return (short) (IConvertible) e;
}
case TypeCode.Int32:
{
return (int) (IConvertible) e;
}
case TypeCode.Int64:
{
return (long) (IConvertible) e;
}
case TypeCode.UInt16:
{
return (ushort) (IConvertible) e;
}
case TypeCode.UInt32:
{
return (uint) (IConvertible) e;
}
case TypeCode.UInt64:
{
return (ulong) (IConvertible) e;
}
case TypeCode.SByte:
{
return (sbyte) (IConvertible) e;
}
}
return 0;
}
}
}
Это фактический класс enum:
namespace TestEnum
{
public class Planet : ACsEnum<Planet, byte, double>
{
#region Base enum
// this holds the position from the sun
public enum planet : byte
{
MERCURY = 1,
VENUS = 2,
EARTH = 3,
MARS = 4,
JUPITER = 5,
SATURN = 6,
URANUS = 7,
NEPTUNE = 8,
PLUTO = 9
}
#endregion
#region ctror
// planet enum, mass, radius, orbital period (earth days)
private Planet( planet p, double m, double r, double v) : base(p, v)
{
Mass = m;
Radius = r;
members.Add(this);
}
#endregion
#region enum specific Properties
public double Mass { get; }
public double Radius { get; }
#endregion
#region enum specific Functions
public static double G = 6.67300E-11;
public double SurfaceGravity()
{
return (G * Mass) / (Radius * Radius);
}
public double SurfaceWeight(double otherMass)
{
return otherMass * SurfaceGravity();
}
#endregion
#region Overrides
public override string ToString() => Enum.ToString();
#endregion
#region Members
// enum mass radius "year"
public static readonly Planet MERCURY = new Planet(planet.MERCURY, 3.303e+23 , 2.4397e6 , 88.0);
public static readonly Planet VENUS = new Planet(planet.VENUS , 4.869e+24 , 6.0518e6 , 224.7);
public static readonly Planet EARTH = new Planet(planet.EARTH , 5.976e+24 , 6.37814e6 , 365.2);
public static readonly Planet MARS = new Planet(planet.MARS , 6.421e+23 , 3.3972e6 , 687.0);
public static readonly Planet JUPITER = new Planet(planet.JUPITER, 1.9e+27 , 7.1492e7 , 4331.0);
public static readonly Planet SATURN = new Planet(planet.SATURN , 5.688e+26 , 6.0268e7 , 10747.0);
public static readonly Planet URANUS = new Planet(planet.NEPTUNE, 8.686e+25 , 2.5559e7 , 30589.0);
public static readonly Planet NEPTUNE = new Planet(planet.URANUS , 1.024e+26 , 2.4746e7 , 59800.0);
public static readonly Planet PLUTO = new Planet(planet.PLUTO , 11.30900e+22 , 1.187e6 , 90560.0);
#endregion
}
}
Наконец, вот пример использования:
using System;
using static TestEnum.Planet;
namespace TestEnum
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.EnumTest();
Console.WriteLine("\n\nWaiting ...: ");
Console.ReadKey();
}
private void EnumTest()
{
// test:
// each admin property: count, ordinal, value, name
// each unique property: APlanet, Mass, Radius, G
// implicit operator
// admin functions:
// admin overrides:
// unique properties
// unique functions
Console.WriteLine("\nadmin properties\n");
Console.WriteLine("count | " + Planet.Count + " (== 9)");
Console.WriteLine("ordinal | " + MERCURY.Ordinal + " (== 0)");
Console.WriteLine("name | " + MERCURY.Name + " (== MERCURY)");
Console.WriteLine("value | " + MERCURY.Value + " (== 88 Mercury year)");
double x = EARTH;
Console.WriteLine("\nadmin Operator\n");
Console.WriteLine("Year | " + x + " (== 365.2 year)");
Console.WriteLine("\nadmin functions\n");
Console.WriteLine("Compare to| " + MERCURY.CompareTo(EARTH) + " (== -1)");
Console.WriteLine("IsMember | " + Planet.IsMember("EARTH", true) + " (== true)");
Console.WriteLine("Find | " + Planet.Find("EARTH", true).Name + " (== EARTH)");
Console.WriteLine("ValueOf | " + Planet.ValueOf("EARTH").Name + " (== EARTH)");
Console.WriteLine("Equals | " + EARTH.Equals(MERCURY) + " (== false => EARTH != MERCURY)");
Console.WriteLine("\n\nunique admin\n");
Console.WriteLine("G | " + Planet.G);
Console.WriteLine("\nunique properties\n");
Console.WriteLine("Enum | " + MERCURY.Enum);
Console.WriteLine("Mass | " + MERCURY.Mass);
Console.WriteLine("Radius | " + MERCURY.Radius);
Console.WriteLine("amount | " + MERCURY.Amount + " (== 1 MERCURY first planet)");
Console.WriteLine("\n\nunique functions");
// typical Java enum usage example
double earthWeight = 175; // lbs
double mass = earthWeight / EARTH.SurfaceGravity();
Console.WriteLine("\ncalc weight via foreach\n");
foreach (Planet p in Planet.Members())
{
Console.WriteLine("Your weight on {0} is {1:F5}",
p.Name, p.SurfaceWeight(mass));
}
// end, typical Java enum usage example
// test Values
Planet[] planets = Planet.Values();
Console.WriteLine("\ncalc weight via array\n");
foreach (Planet p in planets)
{
Console.WriteLine("Your weight on {0} is {1:F5}",
p.Name, p.SurfaceWeight(mass));
}
// test switch
Planet planet = PLUTO;
Console.WriteLine("\nuse switch - looking for PLUTO\n");
switch (planet.Enum)
{
case Planet.planet.EARTH:
{
Console.WriteLine("found EARTH\n");
break;
}
case Planet.planet.JUPITER:
{
Console.WriteLine("found JUPITER\n");
break;
}
case Planet.planet.PLUTO:
{
Console.WriteLine("found PLUTO\n");
break;
}
}
// these will use implicit value
Console.WriteLine("\ntest comparison checks\n");
if (EARTH == EARTH)
{
Console.WriteLine("\npassed - EARTH == EARTH\n");
}
if (MERCURY < EARTH)
{
Console.WriteLine("passed - MERCURY < EARTH\n");
}
if (PLUTO > EARTH)
{
Console.WriteLine("passed - PLUTO > EARTH\n");
}
// test enum extension
Console.WriteLine("\nbonus - enum extension\n");
Console.WriteLine("PLUTO AsShort| " + Planet.planet.PLUTO.Value() + " (9th planet)");
// test ValueOf failure
Console.WriteLine("\n\nValueOf that fails\n");
try
{
planet = Planet.ValueOf("xxx");
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
Я надеюсь, что это поможет любому, кто хочет предоставить дополнительные функции enum, и, возможно, это поможет кому-то преобразовать их программу из Java в C #