Choisir la valeur par défaut d'un type Enum sans avoir à modifier les valeurs

209

En C #, est-il possible de décorer un type Enum avec un attribut ou de faire autre chose pour spécifier quelle devrait être la valeur par défaut, sans avoir à changer les valeurs? Les nombres requis peuvent être gravés dans la pierre pour une raison quelconque, et il serait pratique d'avoir toujours le contrôle sur la valeur par défaut.

enum Orientation
{
    None = -1,
    North = 0,
    East = 1,
    South = 2,
    West = 3
}

Orientation o; // Is 'North' by default.
xyz
la source
Devriez-vous être en mesure de modifier la valeur par défaut pour int?, Double ?, etc.?
AR
2
@AR Ne vous offusque pas, mais il n'est pas logique de comparer les énumérations aux ints dans l'abstrait , simplement parce qu'elles se trouvent être implémentées en tant que types numériques. Nous pourrions implémenter des énumérations sous forme de chaînes, et cela ne changerait pas leur utilité. Je considère la réponse ici comme une limitation de l'expressivité du langage C #; la limitation n'est certainement pas inhérente à l'idée de «distinguer les valeurs d'un ensemble restreint».
jpaugh
1
@jpaugh Je ne compare pas les énumérations aux ints "dans l'abstrait". Je donne des exemples d'autres types de données (y compris des chaînes et d'autres références) où la modification des valeurs par défaut n'a aucun sens. Ce n'est pas une limitation, c'est une fonctionnalité de conception. La cohérence est votre amie :)
AR
@AR Point pris. Je suppose que je passe trop de temps à penser à enums comme des constructions de niveau type, spécifiquement comme des types de somme (ou voir ici . Autrement dit, considérons chaque énumération comme un type distinct, où toutes les valeurs de ce type sont distinctes de toutes les autres valeurs d'énumération. Dans un tel état d'esprit, il est impossible pour les énumérations de partager une valeur, et en effet, cette abstraction se désagrège lorsque vous considérez que la valeur par défaut de 0mai ou peut ne pas être valide pour une énumération donnée, et est simplement à cornes de chaussure dans chaque énumération.
jpaugh
Oui, je connais les syndicats discriminés. Les énumérations C # ne le sont pas, donc je ne sais pas s'il est utile de les penser à un niveau aussi élevé (et incorrect). Alors qu'un «type de somme» a certainement ses avantages, il en va de même pour l'énumération (en supposant que les valeurs appropriées sont fournies). par exemple myColor = Colors.Red | Couleurs Bleu. Mon point principal est que si vous voulez un type de somme pour c #, faites-en un plutôt que d'essayer de réutiliser le concept d'une énumération.
AR

Réponses:

350

La valeur par défaut pour un enum(en fait, n'importe quel type de valeur) est 0 - même si ce n'est pas une valeur valide pour cela enum. Cela ne peut pas être changé.

James Curran
la source
13
Reformuler la question de demander que la valeur par défaut pour un int soit 42. Pensez comment daft que les sons ... La mémoire est obturées par le moteur d' exécution avant de l' obtenir, les énumérations sont struct rappelez - vous
ShuggyCoUk
3
@DougS 1. Non, mais admettre que vous aviez tort l'est. 2. Il n'a gagné aucune réputation grâce aux commentaires positifs.
Aske B.
7
Les commentaires m'ont fait douter de cette réponse, alors je l'ai testée et vérifié que cette réponse est définitivement correcte.
Ian Newson
1
@JamesCurran Mais lorsque je n'utilise pas de valeurs 0 pour les énumérations, je rencontre "Une exception de type 'System.NullReferenceException' s'est produite dans MyProjectName.dll mais n'a pas été gérée dans le code utilisateur. Informations supplémentaires: La référence d'objet n'est pas définie sur une instance d'un objet." Erreur. Comment le réparer? Remarque: J'utilise une méthode d'extension afin d'afficher les descriptions d'énumération, mais je ne sais pas comment éviter l'erreur dans la méthode d'assistance (GetDescription) définie sur cette page
Jack
65

La valeur par défaut de toute énumération est zéro. Donc, si vous souhaitez définir un énumérateur comme valeur par défaut, définissez-le sur zéro et tous les autres énumérateurs sur non-zéro (le premier énumérateur à avoir la valeur zéro sera la valeur par défaut de cette énumération s'il existe plusieurs énumérateurs). avec la valeur zéro).

enum Orientation
{
    None = 0, //default value since it has the value '0'
    North = 1,
    East = 2,
    South = 3,
    West = 4
}

Orientation o; // initialized to 'None'

Si vos énumérateurs n'ont pas besoin de valeurs explicites, assurez-vous simplement que le premier énumérateur est celui que vous souhaitez être l'énumérateur par défaut, car "Par défaut, le premier énumérateur a la valeur 0 et la valeur de chaque énumérateur successif est augmentée de 1." ( Référence C # )

enum Orientation
{
    None, //default value since it is the first enumerator
    North,
    East,
    South,
    West
}

Orientation o; // initialized to 'None'
Empereur XLII
la source
38

Si zéro ne fonctionne pas comme valeur par défaut appropriée, vous pouvez utiliser le modèle de composant pour définir une solution de contournement pour l'énumération:

[DefaultValue(None)]
public enum Orientation
{
     None = -1,
     North = 0,
     East = 1,
     South = 2,
     West = 3
 }

public static class Utilities
{
    public static TEnum GetDefaultValue<TEnum>() where TEnum : struct
    {
        Type t = typeof(TEnum);
        DefaultValueAttribute[] attributes = (DefaultValueAttribute[])t.GetCustomAttributes(typeof(DefaultValueAttribute), false);
        if (attributes != null &&
            attributes.Length > 0)
        {
            return (TEnum)attributes[0].Value;
        }
        else
        {
            return default(TEnum);
        }
    }
}

puis vous pouvez appeler:

Orientation o = Utilities.GetDefaultValue<Orientation>();
System.Diagnostics.Debug.Print(o.ToString());

Remarque: vous devrez inclure la ligne suivante en haut du fichier:

using System.ComponentModel;

Cela ne change pas la valeur par défaut réelle du langage C # de l'énumération, mais donne un moyen d'indiquer (et d'obtenir) la valeur par défaut souhaitée.

David
la source
désolé, cela ne fonctionne pas pour vous. Pouvez-vous ajouter plus d'informations? vous devez ajouter la ligne à l'aide de System.ComponentModel; puis écrivez la fonction GetDefaultEnum () dans une classe Utilities. Quelle version de .net utilisez-vous?
David
3
+1 pour la réutilisation de l'attribut de modèle de composant et la solution propre. Je voudrais vous recommander de changer la dernière ligne de la elsedéclaration comme suit: return default(TEnum);. Premièrement, cela réduira les Enum.GetValues(...).***appels plus lents , en second lieu (et plus important), il sera générique pour tout type, même les non-énumérations. De plus, vous ne restreignez pas le code à utiliser uniquement pour les énumérations, donc cela corrigera en fait les exceptions potentielles lorsqu'il est utilisé sur des types non énumérés.
Ivaylo Slavov
1
Cela peut être une mauvaise utilisation de l'attribut de valeur par défaut dans le contexte de la question posée. La question posée concerne la valeur initialisée automatiquement, qui ne changera pas avec la solution proposée. Cet attribut est destiné à la valeur par défaut d'un concepteur visuel différente de la valeur initiale. Il n'y a aucune mention par la personne posant la question du concepteur visuel.
David Burg
2
Il n'y a vraiment rien dans la documentation MSDN que le ComponentModel ou DefaultAttribute est une fonctionnalité de "concepteur visuel" uniquement. Comme l'indique la réponse acceptée. La réponse courte est "non, c'est impossible" pour redéfinir la valeur par défaut d'une énumération à autre chose que la valeur 0. Cette réponse est une solution de contournement.
David
17

La valeur par défaut d'une énumération correspond à l'énumération égale à zéro. Je ne crois pas que cela soit modifiable par attribut ou par d'autres moyens.

(MSDN dit: "La valeur par défaut d'une énumération E est la valeur produite par l'expression (E) 0.")

Joe
la source
13
Strictement, il n'est même pas nécessaire d'avoir une énumération avec cette valeur. Zéro est toujours la valeur par défaut.
Marc Gravell
11

Vous ne pouvez pas, mais si vous le souhaitez, vous pouvez faire un tour. :)

    public struct Orientation
    {
        ...
        public static Orientation None = -1;
        public static Orientation North = 0;
        public static Orientation East = 1;
        public static Orientation South = 2;
        public static Orientation West = 3;
    }

utilisation de cette structure comme énumération simple.
où vous pouvez créer pa == Orientation.East (ou toute valeur que vous voulez) par défaut
pour utiliser l'astuce elle-même, vous devez convertir int par code.
là l'implémentation:

        #region ConvertingToEnum
        private int val;
        static Dictionary<int, string> dict = null;

        public Orientation(int val)
        {
            this.val = val;
        }

        public static implicit operator Orientation(int value)
        {
            return new Orientation(value - 1);
        }

        public static bool operator ==(Orientation a, Orientation b)
        {
            return a.val == b.val;
        }

        public static bool operator !=(Orientation a, Orientation b)
        {
            return a.val != b.val;
        }

        public override string ToString()
        {
            if (dict == null)
                InitializeDict();
            if (dict.ContainsKey(val))
                return dict[val];
            return val.ToString();
        }

        private void InitializeDict()
        {
            dict = new Dictionary<int, string>();
            foreach (var fields in GetType().GetFields(BindingFlags.Public | BindingFlags.Static))
            {
                dict.Add(((Orientation)fields.GetValue(null)).val, fields.Name);
            }
        } 
        #endregion
Avram
la source
Dans votre opérateur de conversion implicite dont vous avez besoin value + 1, sinon maintenant vos default(Orientation)retours East. Rendez également le constructeur privé. Bonne approche.
nawfal
10

Une autre façon qui pourrait être utile - d'utiliser une sorte d'alias. Par exemple:

public enum Status
{
    New = 10,
    Old = 20,
    Actual = 30,

    // Use Status.Default to specify default status in your code. 
    Default = New 
}
Dmitry Pavlov
la source
1
La suggestion est bonne, cependant, comme déjà mentionné dans d'autres réponses, les énumérations ont leur valeur par défaut à zéro. Dans votre cas, l'expression Status s = default(Status);ne fera partie d'aucun membre de statut, car selle aura la valeur de (Staus) 0. Ce dernier est valide pour écrire directement, car les énumérations peuvent être converties en entiers, mais il échouera lors de la désérialisation, car le framework s'assure qu'une énumération est toujours désérialisée en un membre valide. Votre code sera parfaitement valide si vous changez la ligne New = 10en New = 0.
Ivaylo Slavov
1
Oui. Nouveau = 0 fonctionnerait. J'éviterais personnellement de ne pas définir explicitement les membres enum.
Dmitry Pavlov
7

En fait, enumla valeur par défaut de an est le premier élément du enumdont la valeur est 0.

Ainsi, par exemple:

public enum Animals
{
    Cat,
    Dog,
    Pony = 0,
}
//its value will actually be Cat not Pony unless you assign a non zero value to Cat.
Animals animal;
Cian
la source
9
Cependant, dans ce cas, "Pony" EST "Cat" (par exemple, Console.WriteLine(Animals.Pony);imprime "Cat")
James Curran
16
En fait, vous devez soit: ne jamais spécifier de valeur OU spécifier toutes les valeurs OU ne spécifier que la valeur du premier membre défini. Voici ce qui se passe: Cat n'a pas de valeur et c'est la 1ère valeur, donc la met automatiquement à 0. Dog n'a pas de valeur, la met automatiquement à PreviousValue + 1 ie 1. Poney a une valeur, laissez la valeur. Donc Cat = Pony = 0, Dog = 1. Si vous avez {Cat, Dog, Pony = 0, Frog} alors Dog = Frog = 1.
user276648
4

The default value of enum is the enummember equal to 0 or the first element(if value is not specified) ... Mais j'ai rencontré des problèmes critiques en utilisant enum dans mes projets et surmonté en faisant quelque chose ci-dessous ... Comment mon besoin était-il lié au niveau de la classe ...

    class CDDtype
    {
        public int Id { get; set; }
        public DDType DDType { get; set; }

        public CDDtype()
        {
            DDType = DDType.None;
        }
    }    


    [DefaultValue(None)]
    public enum DDType
    {       
        None = -1,       
        ON = 0,       
        FC = 1,       
        NC = 2,       
        CC = 3
    }

et obtenir le résultat attendu

    CDDtype d1= new CDDtype();
    CDDtype d2 = new CDDtype { Id = 50 };

    Console.Write(d1.DDType);//None
    Console.Write(d2.DDType);//None

Maintenant, que se passe-t-il si la valeur provient de DB ... Ok dans ce scénario ... passez la valeur dans la fonction ci-dessous et cela convertira la valeur enum ... sous la fonction gérée divers scénarios et c'est générique ... et croyez-moi, c'est très vite ..... :)

    public static T ToEnum<T>(this object value)
    {
        //Checking value is null or DBNull
        if (!value.IsNull())
        {
            return (T)Enum.Parse(typeof(T), value.ToStringX());
        }

        //Returanable object
        object ValueToReturn = null;

        //First checking whether any 'DefaultValueAttribute' is present or not
        var DefaultAtt = (from a in typeof(T).CustomAttributes
                          where a.AttributeType == typeof(DefaultValueAttribute)
                          select a).FirstOrNull();

        //If DefaultAttributeValue is present
        if ((!DefaultAtt.IsNull()) && (DefaultAtt.ConstructorArguments.Count == 1))
        {
            ValueToReturn = DefaultAtt.ConstructorArguments[0].Value;
        }

        //If still no value found
        if (ValueToReturn.IsNull())
        {
            //Trying to get the very first property of that enum
            Array Values = Enum.GetValues(typeof(T));

            //getting very first member of this enum
            if (Values.Length > 0)
            {
                ValueToReturn = Values.GetValue(0);
            }
        }

        //If any result found
        if (!ValueToReturn.IsNull())
        {
            return (T)Enum.Parse(typeof(T), ValueToReturn.ToStringX());
        }

        return default(T);
    }
Moumit
la source
-1 pour ne pas avoir intentionnellement répondu à la question donnée. Ce n'est pas un problème de devoirs évident, ce qui devrait être le seul cas où vous ne répondez pas intentionnellement.
Xynariz
1
@Xynariz .. désolé les gars .. mes commentaires étaient négatifs par erreur car ma véritable intention était de fournir une solution d'une autre manière ... J'ai donc changé mes commentaires ...: D
Moumit
2

La valeur par défaut pour un type d'énumération est 0:

  • " Par défaut, le premier énumérateur a la valeur 0 et la valeur de chaque énumérateur successif est augmentée de 1. "

  • " Le type de valeur enum a la valeur produite par l'expression (E) 0, où E est l'identificateur d'énumération. "

Vous pouvez consulter la documentation de l'énumération C # ici et la documentation de la table des valeurs par défaut C # ici .

acoelhosantos
la source
1

Si vous définissez l'énumération par défaut comme l'énumération avec la plus petite valeur, vous pouvez utiliser ceci:

public enum MyEnum { His = -1, Hers = -2, Mine = -4, Theirs = -3 }

var firstEnum = ((MyEnum[])Enum.GetValues(typeof(MyEnum)))[0];

firstEnum == Mine.

Cela ne suppose pas que l'énumération a une valeur nulle.

Tal Segal
la source
0

La valeur par défaut est la première de la définition. Par exemple:

public enum MyEnum{His,Hers,Mine,Theirs}

Enum.GetValues(typeOf(MyEnum)).GetValue(0);

Cela reviendra His

Tony Hopkinson
la source
haha; La valeur par défaut est zéro. MAIS! dit-il, la valeur par défaut "zéro" n'est que le symbole par défaut de la première valeur nommée; ! en d'autres termes, la langue choisit la valeur par défaut au lieu de vous; cela arrive aussi simplement que zéro est la valeur par défaut d'un int. lol
user1953264
0
enum Orientations
{
    None, North, East, South, West
}
private Orientations? _orientation { get; set; }

public Orientations? Orientation
{
    get
    {
        return _orientation ?? Orientations.None;
    }
    set
    {
        _orientation = value;
    }
}

Si vous définissez la propriété sur null retournera Orientations.Aucun sur get. La propriété _orientation est nulle par défaut.

Faruk A Feres
la source
-5
[DefaultValue(None)]
public enum Orientation
{
    None = -1,
    North = 0,
    East = 1,
    South = 2,
    West = 3
}

Ensuite, dans le code, vous pouvez utiliser

public Orientation GetDefaultOrientation()
{
   return default(Orientation);
} 
Ruslan Urban
la source
14
/! \ WRONG /! \ Le DefaultValueAttribute ne définit pas réellement la valeur, c'est juste à des fins d'information seulement. Ensuite, c'est à vous de vérifier si votre énumération, champ, propriété, ... a cet attribut. Le PropertyGrid l'utilise pour que vous puissiez cliquer avec le bouton droit et sélectionner "Réinitialiser", également si vous n'utilisez pas la valeur par défaut, le texte est affiché en gras - MAIS - vous devez toujours définir manuellement votre propriété à la valeur par défaut que vous souhaitez.
user276648