是否可以复制在enum中提供抽象方法的功能,内部枚举常量必须覆盖并提供功能?
public enum Logic {
PAY_DAY {
@Override
public void acceptPlayer(Player player) {
// Perform logic
}
},
COLLECT_CASH {
@Override
public void acceptPlayer(Player player) {
// Perform logic
}
}
,
ETC_ETC {
@Override
public void acceptPlayer(Player player) {
// Perform logic
}
};
public abstract void acceptPlayer(Player player);
}
如果不能:: 你能提供一种方法,我可以用类似的方式实现很多特定的逻辑吗?
编辑::我知道C#中的枚举并不像Java那样真正的“对象”,但我希望执行类似的逻辑。
编辑::为了澄清,我不想为每个特定的逻辑位提供具体的类。 IE,创建一个接口acceptPlayer并创建许多新类是不合适的
答案 0 :(得分:5)
这是一个选项 - 不使用枚举,而是类似的东西......
public abstract class Logic
{
public static readonly Logic PayDay = new PayDayImpl();
public static readonly Logic CollectCash = new CollectCashImpl();
public static readonly Logic EtcEtc = new EtcEtcImpl();
// Prevent other classes from subclassing
private Logic() {}
public abstract void AcceptPlayer(Player player);
private class PayDayImpl : Logic
{
public override void AcceptPlayer(Player player)
{
// Perform logic
}
}
private class CollectCashImpl : Logic
{
public override void AcceptPlayer(Player player)
{
// Perform logic
}
}
private class EtcEtcImpl : Logic
{
public override void AcceptPlayer(Player player)
{
// Perform logic
}
}
}
你说你不想为每一个逻辑提供一个具体的类 - 但这基本上就是你用Java做的事情,只是这个类会稍微对你隐瞒。
以下是使用委托处理不同行为的替代方法:
public sealed class Logic
{
public static readonly Logic PayDay = new Logic(PayDayAccept);
public static readonly Logic CollectCash = new Logic(CollectCashAccept);
public static readonly Logic EtcEtc = new Logic(player => {
// An alternative using lambdas...
});
private readonly Action<Player> accept;
private Logic(Action<Player> accept)
{
this.accept = accept;
}
public void AcceptPlayer(Player player)
{
accept(player);
}
private static void PayDayAccept(Player player)
{
// Logic here
}
private static void CollectCashAccept(Player player)
{
// Logic here
}
}
在这两种情况下,您仍然可以获得一组固定的值 - 但您将无法打开它们。你可能有一个单独的“真实”枚举,但那会有点混乱。
答案 1 :(得分:2)
如果您不想使用接口和类层次结构,则可以使用委托,例如:
public class Player{}
public static class Logic
{
public static readonly Action<Player> PAY_DAY = p => Console.WriteLine( "Pay day : " + p.ToString());
public static readonly Action<Player> COLLECT_CASH = p=> Console.WriteLine(p.ToString ());
public static void AcceptPlayer( this Action<Player> PlayerAction, Player ActingPlayer )
{
PlayerAction(ActingPlayer);
}
}
class MainClass
{
public static void Main (string[] args)
{
var player = new Player();
Logic.PAY_DAY.AcceptPlayer( player );
}
}
答案 2 :(得分:0)
在c#中,您使用类而不是枚举来执行此操作。使用要覆盖的虚拟方法创建基类。
Java enum的每个成员都将是一个具有所需实现的子类。
最后,在每个枚举成员的基类中创建一个只读静态字段,并使用该枚举值的子类实例对其进行初始化。
答案 3 :(得分:0)
我喜欢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
}
}
这是扩展方法,可帮助枚举:
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;
}
}
}
这是实际的“枚举”类:
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);
}
}
}
}
我希望这对希望为枚举提供更多功能的人有所帮助,也许这将有助于某人将其程序从Java转换为C#
答案 4 :(得分:0)
扩展answer by Jon Skeet时,我发现了另一种方法,该方法甚至允许您使用枚举类似的结构来处理个人行为/数据。这可能不是一个真正的问题答案,因为它无法实现:
编辑::为了澄清,我不想为每个特定的逻辑位提供具体的类。 IE,创建接口acceptPlayer并创建许多新类是不合适的
以下结构与C#7一起提供了类似于枚举的语法:
public abstract class Logic
{
private Logic() { }
public abstract void acceptPlayer(Player player);
public sealed class PayDay : Logic
{
public PayDay(DateTime timeOfPayment)
{
TimeOfPayment = timeOfPayment;
}
public DateTime TimeOfPayment { get; }
public override void acceptPlayer(Player player)
{
// Perform logic
}
}
public sealed class CollectCash : Logic
{
public CollectCash(int amountOfMoney)
{
AmountOfMoney = amountOfMoney;
}
public int AmountOfMoney { get; }
public override void acceptPlayer(Player player)
{
// Perform logic
}
}
public sealed class EtcEtc : Logic
{
public override void acceptPlayer(Player player)
{
// Perform logic
}
};
}
抽象Logic类的私有构造函数和密封实现保证了只有定义明确的选项集才是Logic类范围的一部分。
创作看起来有些不寻常:
Logic logic = new Logic.PayDay(DateTime.Now);
但是与C#7的模式匹配一起,它甚至开始感觉像枚举:
switch (logic)
{
case Logic.PayDay payday:
var timeOfPayment = payday.TimeOfPayment;
// ...
break;
case Logic.CollectCash collectCash:
// ...
break;
// ...
}
其他测试逻辑也很容易理解:
if (logic is Logic.EtcEtc)
{
// ...
}
或者如果您需要强制转换的实例:
if (logic is Logic.EtcEtc etcEtc)
{
// ...
}