在C#中实现枚举对象类型

本文关键字:对象 类型 枚举 实现 | 更新日期: 2023-09-27 18:20:15

我是C#的新手,但我用Java做了很多编程。在Java中,可以通过声明一个具有多个字段和一个私有构造函数的enum来创建枚举对象类型(一个只有固定数量的可能值的类)。例如,本Java教程展示了如何创建枚举的"Planet"类型,这是一个对象(它有多个数据字段),但具有固定数量的值(只有8个行星,因此该对象只有8个实例)。

然而,在C#中似乎没有任何方法可以做到这一点,因为C#中的enum只允许是基元类型,特别是数字基元类型。有没有什么简单的方法可以强制C#对象类型具有固定数量的可能实例值?或者我应该采用某种Singleton Pattern/Factory Pattern架构,比如给类一个私有构造函数,并定义固定数量的静态方法来返回一个有效实例?

在C#中实现枚举对象类型

System.Drawing.Color就是这种事情的一个例子。

Color类型具有Color类型的静态属性集合,这些静态属性提供对命名颜色的访问。例如,System.Drawing.Color.CornflowerBlue

Color类型允许您在不使用命名的颜色静态成员的情况下创建Color。如果你想禁止这种情况,你可以让你的类只有私有构造函数。那么,访问您类型的实例的唯一方法是选择您类型的一个可用静态属性。

您可以创建一个静态类,该类具有表示您的值的静态属性。这可能是最接近的。

public static class Planets
{
    // static cctor
    static Planets()
    {
        Saturn = new SaturnPlanet();
        Earth = new EarthPlanet();
        ...
    }
    public static Planet Saturn { get; private set; }
    public static Planet Earth { get; private set; }
    ...
}

您也可以选择在此处公开枚举器或IEnumerable方法或属性。

为了保持理智,Planets类及其所有属性对象(在本例中为Planet类型)都应该是不可变的。否则,您可能会遇到一些恶劣的多线程竞争条件。

枚举从System.Enum继承,并且是密封的(不能从枚举继承)。进一步的枚举是积分类型(short、int、long等)之上的语法糖

public enum Planet
{
    Mercury = 1 ,
    Venus   = 2 ,
    Earth   = 3 ,
    Mars    = 4 ,
    Jupiter = 5 ,
    Saturn  = 6 ,
    Uranus  = 7 ,
    Neptune = 8 ,
}
public interface IPlanetData
{
    double Mass   { get ; }
    double Radius { get ; }
}
public static class PlanetEnumHelpers
{
    private class PlanetData : IPlanetData
    {
        internal PlanetData( double mass , double radius )
        {
            Mass = mass ;
            Radius = radius ;
        }
        public double Mass   { get ; private set ; }
        public double Radius { get ; private set ; }
    }
    public static IPlanetData Data( this Planet planet )
    {
        IPlanetData instance ;
        switch ( planet )
        {
            case Planet.Mercury : instance = Mercury ; break ;
            case Planet.Venus   : instance = Venus   ; break ;
            case Planet.Earth   : instance = Earth   ; break ;
            case Planet.Mars    : instance = Mars    ; break ;
            case Planet.Jupiter : instance = Jupiter ; break ;
            case Planet.Saturn  : instance = Saturn  ; break ;
            case Planet.Uranus  : instance = Uranus  ; break ;
            case Planet.Neptune : instance = Neptune ; break ;
            default : throw new ArgumentOutOfRangeException("planet") ;
        }
        return instance ;
    }
    private static IPlanetData Mercury = new PlanetData( 3.303e+23 , 2.4397e6  ) ;
    private static IPlanetData Venus   = new PlanetData( 4.869e+24 , 6.0518e6  ) ;
    private static IPlanetData Earth   = new PlanetData( 5.976e+24 , 6.37814e6 ) ;
    private static IPlanetData Mars    = new PlanetData( 6.421e+23 , 3.3972e6  ) ;
    private static IPlanetData Jupiter = new PlanetData( 1.9e+27   , 7.1492e7  ) ;
    private static IPlanetData Saturn  = new PlanetData( 5.688e+26 , 6.0268e7  ) ;
    private static IPlanetData Uranus  = new PlanetData( 8.686e+25 , 2.5559e7  ) ;
    private static IPlanetData Neptune = new PlanetData( 1.024e+26 , 2.4746e7  ) ;
}
class Program
{
    static void Main( string[] args )
    {
        foreach ( Planet p in Enum.GetValues( typeof( Planet ) ) )
        {
            Console.WriteLine( "{0}: Mass={1} Radius={2}" , p , p.Data().Mass , p.Data().Radius );
        }
    }
}

这有点像黑客,但保留了Enum的语义(例如,在switch语句中使用它的能力。