EnumCzęść NR.15

Typ wyliczeniowy jest to typ, który zawiera określoną liczbę nazwanych stałych. Aby utworzyć typ wyliczeniowy wystarczy użyć słowa kluczowego “enum” zamiast “class”.

Wewnątrz nawiasów klamrowych po przecinku definiujemy listę stałych elementów. Zasady poziomu dostępu typu wyliczeniowego są takie same jak zasady dla klasy.

W Javie stałe wartości enum są pisane z dużych liter.

enum Directions
{
    EAST,WEST,SOUTH,NORTH
}

Obiekt enum typu zadeklarowanego powyżej może przetrzymywać tylko jedną z wartości określonych w tym typie wyliczeniowym. Stałe typu wyliczeniowego są dostępne w podobny sposób jak statyczne pola w klasie.

Directions d = Directions.EAST;

Podstawowa Deklaracja enum w C# jest identyczna. W C# jednak nie ma zasady by pisać elementy typu wyliczeniowego z dużych liter.enum State { Run, Offline };

Enum w Javie mogą wiele

W Javie typ wyliczeniowy ma wiele właściwości, które nie są obecne w innych językach jak C# czy C++.

W Javie typ wyliczeniowy jest w pewnym sensie specjalnym typem klasy i może on zawierać wszystko, co klasa Javowa może.

Przykładowo nie ma problemu jeśli chciałbym dodać pole do typu wyliczeniowego. Pole, które określałoby numer kierunku.

Po deklaracji stałych typu wyliczeniowego stawiam myślnik, a potem mogę definiować elementy klas.

enum Directions
{
    EAST,WESTE,SOUTH,NORTH;
    public int number;
}

Aby ustawić pole będzie mi potrzebny konstruktor

image

Po dodaniu konstruktora muszę do stałych przypisać odpowiednie wartości parametrów dopiero co dodanego konstruktora.

image

Konstruktor w typie wyliczeniowym musi być prywatny. W końcu tworzymy typ wyliczeniowy na podstawie wybrania jego stałej, a nie na podstawie konstruktora.

enum Directions
{
    EAST(1),WEST(2),SOUTH(3),NORTH(4);
    
    private int number;
    
    Directions(int n) {
        number = n;
    }
}

Jeśli wybierzemy wartość EAST z typu wyliczeniowego, oznacza to, że ustawiamy wartość pola number na 1.

Directions d = Directions.EAST

Istnieje jeszcze jedna różnica pomiędzy typem wyliczeniowym a normalną klasą Javową. Typy wyliczeniowe dziedziczą niejawnie po klasie java.lang.Enum.

Daje to typom wyliczeniowym dwie metody statyczne.

  • Values
  • ValueOf

    Metoda Values zwraca tablice wszystkich stałych elementów. Metoda ValueOf zwraca napis stałej typu wyliczeniowego.

    Directions[] a = Directions.values();
    Directions s = Directions.valueOf(a[0].toString()); // Directions.EAST
    
    

    Nie można nadawać numerów kolejności typom wyliczeniowym w Javie. W C# jest to możliwe.

    image

    Nie zmienia to faktu, że Java może wiele przy użyciu swoich typów wyliczeniowych. Oto przykład z dokumentacji Javy

    public enum Planet {
        MERCURY (3.303e+23, 2.4397e6),
        VENUS   (4.869e+24, 6.0518e6),
        EARTH   (5.976e+24, 6.37814e6),
        MARS    (6.421e+23, 3.3972e6),
        JUPITER (1.9e+27,   7.1492e7),
        SATURN  (5.688e+26, 6.0268e7),
        URANUS  (8.686e+25, 2.5559e7),
        NEPTUNE (1.024e+26, 2.4746e7);
    
        // in kilograms
        private final double mass;
        // in meters
        private final double radius;
        Planet(double mass, double radius) {
            this.mass = mass;
            this.radius = radius;
        }
        private double mass() { return mass; }
        private double radius() { return radius; }
    
        // universal gravitational 
        // constant  (m3 kg-1 s-2)
        public static final double G = 6.67300E-11;
    
        double surfaceGravity() {
            return G * mass / (radius * radius);
        }
        double surfaceWeight(double otherMass) {
            return otherMass * surfaceGravity();
        }
        public static void main(String[] args) {
            if (args.length != 1) {
                System.err.println("Usage: java Planet <earth_weight>");
                System.exit(-1);
            }
            double earthWeight = Double.parseDouble(args[0]);
            double mass = earthWeight/EARTH.surfaceGravity();
            for (Planet p : Planet.values())
               System.out.printf("Your weight on %s is %f%n",
                                 p, p.surfaceWeight(mass));
        }
    }
    
    

    Enum w C#

    W C# istnieje możliwość numeracji kolejności typu wyliczeniowego.

    Domyślnie kolejność wygląda tak:

    enum State
    {
        Run, // 0
        Offline // 1
    };

    Możemy przypisać swoje własne wartości. Czasami, aby było łatwiej możemy skorzystać z operacji dodawania do istniejących już elementów typu wyliczeniowego.

    enum State
    {
    Run = 0,  , Offline = Run + 1
    };
    
    

    W C# typy wyliczeniowe niejawnie mogą przetrzymywać tyle elementów, co typ INT. Można to jednak nadpisać.

    enum MyEnum : byte {};
    
    

    Stałe typu wyliczeniowego mogą być rzutowane na typ INT. Aby uzyskać nazwę typu wyliczeniowego korzystamy z metody ToString().

    static void Main()
    {
        State s = State.Run;
        int i = (int)s; // 0
        string t = s.ToString(); // Run
    }
    
    

    Inne metody mogą być odnalezione w klasie statycznej Enum.

    image

    Nazwy metod są samoobjaśniające się.

    string e1 = Enum.GetName(typeof(State), State.Offline);
    string[] e2 = Enum.GetNames(typeof(State));
    Array e3 = Enum.GetValues(typeof(State));
    bool e4 = Enum.IsDefined(typeof(State), 121);
    Object e5 = Enum.Parse(typeof(State), "Offline");
    Object e6 = Enum.ToObject(typeof(State), 0);
    
    

    Co prawda nie można utworzyć metod wewnątrz Typu wyliczeniowego w C#, ale można stworzyć metodę rozszerzeniową.

    public static class ExtensionStateMethods
    {
        public static int GetMyNumber(this State s)
        {
            switch (s)
            {
                case State.Run:
                    return 12;
    
                case State.Offline:
                    return 14;
    
            }
    
            return 0;
        }
    }
    
    

    Można w ten sposób zasymulować działanie Javy. Jednakże przy zawansowanych przypadkach gdybyśmy portowali typ wyliczeniowy Javy na C# to lepiej zastosować klasę, która ma niezmienne pola.

    Oto przykład zawansowanego typu wyliczeniowego z Javy przeportowanego na C# jako klasę.

    public class Planet
    {
        public static readonly Planet MERCURY = new Planet("Mercury", 3.303e+23, 2.4397e6);
        public static readonly Planet VENUS = new Planet("Venus", 4.869e+24, 6.0518e6);
        public static readonly Planet EARTH = new Planet("Earth", 5.976e+24, 6.37814e6);
        public static readonly Planet MARS = new Planet("Mars", 6.421e+23, 3.3972e6);
        public static readonly Planet JUPITER = new Planet("Jupiter", 1.9e+27, 7.1492e7);
        public static readonly Planet SATURN = new Planet("Saturn", 5.688e+26, 6.0268e7);
        public static readonly Planet URANUS = new Planet("Uranus", 8.686e+25, 2.5559e7);
        public static readonly Planet NEPTUNE = new Planet("Neptune", 1.024e+26, 2.4746e7);
        public static readonly Planet PLUTO = new Planet("Pluto", 1.27e+22, 1.137e6);
    
        public static IEnumerable<Planet> Values
        {
            get
            {
                yield return MERCURY;
                yield return VENUS;
                yield return EARTH;
                yield return MARS;
                yield return JUPITER;
                yield return SATURN;
                yield return URANUS;
                yield return NEPTUNE;
                yield return PLUTO;
            }
        }
    
        private readonly string name;
        private readonly double mass;   // in kilograms
        private readonly double radius; // in meters
    
        Planet(string name, double mass, double radius)
        {
            this.name = name;
            this.mass = mass;
            this.radius = radius;
        }
    
        public string Name { get { return name; } }
    
        public double Mass { get { return mass; } }
    
        public double Radius { get { return radius; } }
    
        // universal gravitational constant  (m3 kg-1 s-2)
        public const double G = 6.67300E-11;
    
        public double SurfaceGravity()
        {
            return G * mass / (radius * radius);
        }
    
        public double SurfaceWeight(double otherMass)
        {
            return otherMass * SurfaceGravity();
        }
    
        public override string ToString()
        {
            return name;
        }
    }
    
    

    …i jego użycie.

    using System;
    using System.Collections.Generic;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                Planet pEarth = Planet.MERCURY;
                double earthRadius = pEarth.Radius; // Just threw it in to show usage
    
                double earthWeight = double.Parse("123");
                double mass = earthWeight / pEarth.SurfaceGravity();
                foreach (Planet p in Planet.Values)
                    Console.WriteLine("Your weight on {0} is {1}", p, p.SurfaceWeight(mass));
    
                Console.ReadKey();
            }
        }
    
     }
    

    Przykład użycia

    Typ wyliczeniowy jest przydatny przy wyrażeniach switch. Daje to programiście łatwe ustalenie jakie wartości są możliwe.

    switch (s)
    {
        case State.Run: break;
        case State.Offline: break;
    }
    
    

    To wszystko.

    switch (s)
    {
        case Directions.EAST break;
        case Directions.WEST: break;
        case Directions.NORTH: break;
        case Directions.SOUTH: break;
    }