是否有可能在c#中模仿这个java enum代码

时间:2012-01-31 20:34:59

标签: c# java enums

是否可以复制在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并创建许多新类是不合适的

5 个答案:

答案 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)
{
    // ...
}