2009-09-03 7 views
24

Quel est l'équivalent de l'énumération de Java en C#?Quel est l'équivalent de l'énumération de Java en C#?

+2

Peut-être que je suis sur la pensée, mais j'ai attendre que vous connaissez déjà ENUM de C# et il y a certaines fonctionnalités avec ENUM de Java que vous faites référence. Si oui, peut-être pourriez-vous élaborer dans votre question. –

+8

Cette question est parfaitement claire si vous connaissez quelque chose à propos de Java 'enum's. –

+0

@Yishai - Dans C# enums par défaut à Int32, mais peut être changé n'importe quel type numérique –

Répondre

34

La fonctionnalité Java enum complète n'est pas disponible en C#. Vous pouvez venir raisonnablement fermer en utilisant des types imbriqués et un constructeur privé si. Par exemple:

using System; 
using System.Collections.Generic; 
using System.Xml.Linq; 

public abstract class Operator 
{ 
    public static readonly Operator Plus = new PlusOperator(); 
    public static readonly Operator Minus = 
     new GenericOperator((x, y) => x - y); 
    public static readonly Operator Times = 
     new GenericOperator((x, y) => x * y); 
    public static readonly Operator Divide = 
     new GenericOperator((x, y) => x/y); 

    // Prevent other top-level types from instantiating 
    private Operator() 
    { 
    } 

    public abstract int Execute(int left, int right); 

    private class PlusOperator : Operator 
    { 
     public override int Execute(int left, int right) 
     { 
      return left + right; 
     } 
    } 

    private class GenericOperator : Operator 
    { 
     private readonly Func<int, int, int> op; 

     internal GenericOperator(Func<int, int, int> op) 
     { 
      this.op = op; 
     } 

     public override int Execute(int left, int right) 
     { 
      return op(left, right); 
     } 
    } 
} 

Bien sûr, vous ne pas ont d'utiliser des types imbriqués, mais ils donnent la pratique partie « comportement personnalisé » qui sont Java énumérations bien pour. Dans d'autres cas, vous pouvez simplement passer des arguments à un constructeur privé pour obtenir un ensemble de valeurs restreint bien connu.

Quelques choses, cela ne vous donne pas:

  • support ordinal
  • commutateur support
  • EnumSet
  • sérialisation/désérialisation (comme singleton)

Certains cela pourrait probablement être fait avec assez d'effort, bien que le changement ne serait pas vraiment possible sans hackery. Maintenant, si le langage a fait quelque chose comme ça, il pourrait faire des choses intéressantes pour faire basculer le hackery automatique (par exemple, déclarer une charge de const champs automatiquement, et en changeant tout commutateur sur le type enum à un entier inverser, seulement autoriser les cas "connus".)

Oh, et les types partiels signifient que vous n'avez pas besoin d'avoir tous les des valeurs enum dans le même fichier. Si chaque valeur est assez impliquée (ce qui est certainement possible), chacun pourrait avoir son propre fichier.

+0

Je pense qu'il serait trivial de prendre en charge les instructions switch (et ordinals) en utilisant une conversion implicite en int, non? Qu'est-ce que j'ai oublié? –

+2

@LimitedAtonement: Comment représenter les valeurs * constant * dont vous auriez besoin pour les expressions de cas? –

+0

J'ai lu récemment votre article sur le fait qu'il y a peu de fois où les champs non-privés sont justifiés. Est-ce un de ces moments? –

-1

enum, ou avez-vous besoin de quelque chose en particulier que Java enums possède mais que C# ne contient pas?

+0

En Java, les énumérations peuvent avoir plusieurs valeurs associées pour chaque énumération, méthodes avec logique et constructeurs. Au moins à partir de C# 3.0, les énumérations C# sont toujours juste une liste de constantes, avec une valeur associée possible. –

+6

-1: Les enums Java sont * tellement * beaucoup plus puissants que ceux en C#. – Kramii

+0

+1 pour contrer les -1 de Kramii. C# enums peut avoir des méthodes d'extension qui compense l'absence de certaines de ces fonctionnalités. – finnw

17

Les énumérations sont l'une des rares fonctionnalités de langage mieux implémentées dans java que C#. Dans java, les énumérations sont des instances nommées complètes d'un type, alors que les C# enums sont essentiellement des constantes nommées. Cela étant dit, dans le cas de base, ils auront l'air similaires. Cependant, dans Java, vous avez plus de puissance, en ce sens que vous pouvez ajouter un comportement aux énumérations individuelles, car ce sont des classes à part entière.

Y at-il une fonctionnalité que vous recherchez?

+8

"Ils fonctionneront de la même manière" - pas IMO, car les nombres constants ne peuvent pas avoir de comportement * personnalisé *, contrairement aux valeurs d'énumération Java. –

+2

yup, d'où la qualification «pour une utilisation de base». Je suis d'accord que l'une des bonnes choses à propos de java enums est le comportement personnalisé – Chi

+1

J'aime Java Enums que C# Enums. Avec C# Enums, vous diffusez beaucoup de Smell, et beaucoup de ces codes dispersés dans les instructions switch pourraient avoir été placés comme des méthodes associées à chaque Enum. – thenonhacker

4

Vous pourriez probablement utiliser l'ancien modèle d'énumération de typesafe que nous avons utilisé en Java avant d'en avoir des réels (en supposant que ceux de C# ne sont pas des classes comme un commentaire). Le modèle est décrit juste avant le milieu de this page

+0

+1: Une approche que j'utilise. – Kramii

5

Voici une autre idée intéressante. Je suis venu avec la classe de base Enumeration suivante:

public abstract class Enumeration<T> 
    where T : Enumeration<T> 
{ 
    protected static int nextOrdinal = 0; 

    protected static readonly Dictionary<int, Enumeration<T>> byOrdinal = new Dictionary<int, Enumeration<T>>(); 
    protected static readonly Dictionary<string, Enumeration<T>> byName = new Dictionary<string, Enumeration<T>>(); 

    protected readonly string name; 
    protected readonly int ordinal; 

    protected Enumeration(string name) 
     : this (name, nextOrdinal) 
    { 
    } 

    protected Enumeration(string name, int ordinal) 
    { 
     this.name = name; 
     this.ordinal = ordinal; 
     nextOrdinal = ordinal + 1; 
     byOrdinal.Add(ordinal, this); 
     byName.Add(name, this); 
    } 

    public override string ToString() 
    { 
     return name; 
    } 

    public string Name 
    { 
     get { return name; } 
    } 

    public static explicit operator int(Enumeration<T> obj) 
    { 
     return obj.ordinal; 
    } 

    public int Ordinal 
    { 
     get { return ordinal; } 
    } 
} 

Il a un paramètre de type essentiellement juste pour que le nombre ordinal fonctionnera correctement sur différents énumérations dérivés.exemple de Jon Operator ci-dessus devient alors:

public class Operator : Enumeration<Operator> 
{ 
    public static readonly Operator Plus = new Operator("Plus", (x, y) => x + y); 
    public static readonly Operator Minus = new Operator("Minus", (x, y) => x - y); 
    public static readonly Operator Times = new Operator("Times", (x, y) => x * y); 
    public static readonly Operator Divide = new Operator("Divide", (x, y) => x/y); 

    private readonly Func<int, int, int> op; 

    // Prevent other top-level types from instantiating 
    private Operator(string name, Func<int, int, int> op) 
     :base (name) 
    { 
     this.op = op; 
    } 

    public int Execute(int left, int right) 
    { 
     return op(left, right); 
    } 
} 

Cela donne quelques avantages.

  • support ordinale
  • conversion à string et int qui rend les états de commutation possibles
  • GetType() donnera le même résultat pour chacune des valeurs d'un type dérivé de dénombrement.
  • Les méthodes statiques de System.Enum peuvent être ajoutées à la classe Enumeration de base pour permettre la même fonctionnalité.
+0

Je suis en train de faire une conversion de Java en C#, alors j'ai regardé votre réponse il y a un mois et j'ai pensé 'un peu trop de problèmes' ... Maintenant, je reviens, en voyant beaucoup plus de valeur la fonction 'retrieve enum by (string) name'. Gloire! –

+0

@Andrew Cooper, re: "Cela donne quelques avantages. * Support ordinale * Conversion en string et int qui rend les instructions switch réalisables" - semble que cela ne donne toujours pas le support des instructions switch, car le compilateur se plaint que les instructions de cas ont besoin d'une valeur constante . Il n'accepte ni la distribution (int) ni la valeur .Ordinal comme const. – tomosius

1
//Review the sample enum below for a template on how to implement a JavaEnum. 
//There is also an EnumSet implementation below. 

public abstract class JavaEnum : IComparable { 
    public static IEnumerable<JavaEnum> Values { 
     get { 
      throw new NotImplementedException("Enumeration missing"); 
     } 
    } 

    public readonly string Name; 

    public JavaEnum(string name) { 
     this.Name = name; 
    } 

    public override string ToString() { 
     return base.ToString() + "." + Name.ToUpper(); 
    } 

    public int CompareTo(object obj) { 
     if(obj is JavaEnum) { 
      return string.Compare(this.Name, ((JavaEnum)obj).Name); 
     } else { 
      throw new ArgumentException(); 
     } 
    } 


    //Dictionary values are of type SortedSet<T> 
    private static Dictionary<Type, object> enumDictionary; 
    public static SortedSet<T> RetrieveEnumValues<T>() where T : JavaEnum { 
     if(enumDictionary == null) { 
      enumDictionary = new Dictionary<Type, object>(); 
     } 
     object enums; 
     if(!enumDictionary.TryGetValue(typeof(T), out enums)) { 
      enums = new SortedSet<T>(); 
      FieldInfo[] myFieldInfo = typeof(T).GetFields(BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public); 
      foreach(FieldInfo f in myFieldInfo) { 
       if(f.FieldType == typeof(T)) { 
        ((SortedSet<T>)enums).Add((T)f.GetValue(null)); 
       } 
      } 
      enumDictionary.Add(typeof(T), enums); 
     } 
     return (SortedSet<T>)enums; 
    } 
} 


//Sample JavaEnum 
public class SampleEnum : JavaEnum { 
    //Enum values 
    public static readonly SampleEnum A = new SampleEnum("A", 1); 
    public static readonly SampleEnum B = new SampleEnum("B", 2); 
    public static readonly SampleEnum C = new SampleEnum("C", 3); 

    //Variables or Properties common to all enums of this type 
    public int int1; 
    public static int int2 = 4; 
    public static readonly int int3 = 9; 

    //The Values property must be replaced with a call to JavaEnum.generateEnumValues<MyEnumType>() to generate an IEnumerable set. 
    public static new IEnumerable<SampleEnum> Values { 
     get { 
      foreach(var e in JavaEnum.RetrieveEnumValues<SampleEnum>()) { 
       yield return e; 
      } 
      //If this enum should compose several enums, add them here 
      //foreach(var e in ChildSampleEnum.Values) { 
      // yield return e; 
      //} 
     } 
    } 

    public SampleEnum(string name, int int1) 
     : base(name) { 
     this.int1 = int1; 
    } 
} 


public class EnumSet<T> : SortedSet<T> where T : JavaEnum { 
    // Creates an enum set containing all of the elements in the specified element type. 
    public static EnumSet<T> AllOf(IEnumerable<T> values) { 
     EnumSet<T> returnSet = new EnumSet<T>(); 
     foreach(T item in values) { 
      returnSet.Add(item); 
     } 
     return returnSet; 
    } 

    // Creates an enum set with the same element type as the specified enum set, initially containing all the elements of this type that are not contained in the specified set. 
    public static EnumSet<T> ComplementOf(IEnumerable<T> values, EnumSet<T> set) { 
     EnumSet<T> returnSet = new EnumSet<T>(); 
     foreach(T item in values) { 
      if(!set.Contains(item)) { 
       returnSet.Add(item); 
      } 
     } 
     return returnSet; 
    } 

    // Creates an enum set initially containing all of the elements in the range defined by the two specified endpoints. 
    public static EnumSet<T> Range(IEnumerable<T> values, T from, T to) { 
     EnumSet<T> returnSet = new EnumSet<T>(); 
     if(from == to) { 
      returnSet.Add(from); 
      return returnSet; 
     } 
     bool isFrom = false; 
     foreach(T item in values) { 
      if(isFrom) { 
       returnSet.Add(item); 
       if(item == to) { 
        return returnSet; 
       } 
      } else if(item == from) { 
       isFrom = true; 
       returnSet.Add(item); 
      } 
     } 
     throw new ArgumentException(); 
    } 

    // Creates an enum set initially containing the specified element(s). 
    public static EnumSet<T> Of(params T[] setItems) { 
     EnumSet<T> returnSet = new EnumSet<T>(); 
     foreach(T item in setItems) { 
      returnSet.Add(item); 
     } 
     return returnSet; 
    } 

    // Creates an empty enum set with the specified element type. 
    public static EnumSet<T> NoneOf() { 
     return new EnumSet<T>(); 
    } 

    // Returns a copy of the set passed in. 
    public static EnumSet<T> CopyOf(EnumSet<T> set) { 
     EnumSet<T> returnSet = new EnumSet<T>(); 
     returnSet.Add(set); 
     return returnSet; 
    } 

    // Adds a set to an existing set. 
    public void Add(EnumSet<T> enumSet) { 
     foreach(T item in enumSet) { 
      this.Add(item); 
     } 
    } 

    // Removes a set from an existing set. 
    public void Remove(EnumSet<T> enumSet) { 
     foreach(T item in enumSet) { 
      this.Remove(item); 
     } 
    } 
} 
Questions connexes