Scelta del valore predefinito di un tipo Enum senza dover modificare i valori


209

In C #, è possibile decorare un tipo Enum con un attributo o fare qualcos'altro per specificare quale dovrebbe essere il valore predefinito, senza cambiare i valori? I numeri richiesti potrebbero essere impostati su pietra per qualsiasi motivo, e sarebbe utile avere ancora il controllo sul valore predefinito.

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

Orientation o; // Is 'North' by default.

Dovresti essere in grado di cambiare il valore predefinito per int ?, double ?, ecc.?
AR,

2
@AR Senza offesa passarti, ma non ha senso confrontare gli enum con ints in astratto , semplicemente perché capita di essere implementati come tipi numerici. Potremmo implementare gli enum come stringhe e ciò non cambierebbe la loro utilità. Considero la risposta qui una limitazione dell'espressività del linguaggio C #; la limitazione non è certamente inerente all'idea di "distinguere i valori da un insieme limitato".
jpaugh

1
@jpaugh Non sto confrontando gli enum con gli ints "in astratto". Sto fornendo esempi di altri tipi di dati (inclusi stringhe e altri riferimenti) in cui la modifica delle impostazioni predefinite non ha senso. Non è un limite, è una funzionalità di progettazione. La coerenza è tua amica :)
AR,

@AR Punto preso. Suppongo di dedicare troppo tempo a pensare a enums come costrutti a livello di tipo, in particolare come tipi di somma (o vedere qui . Cioè, considerare ogni enum come un tipo distinto, in cui tutti i valori di quel tipo sono distinti dai valori di ogni altro enum. Da una tale mentalità, è impossibile per gli enum condividere qualsiasi valore, e in effetti, questa astrazione cade a pezzi quando si considera che il default di 0può o non può essere valido per un dato enum, ed è semplicemente calpestato in ogni enum.
jpaugh

Sì, ho familiarità con i sindacati discriminati. Gli Enum C # non lo sono, quindi non so se sia utile pensarli a un livello così alto (e non corretto). Mentre un "tipo di somma" presenta certamente i suoi vantaggi, lo stesso vale per enum (presupponendo che siano forniti valori appropriati). ad esempio myColor = Colors.Red | Colors.Blue. Il mio punto principale è che se vuoi un tipo di somma per c #, creane uno, piuttosto che cercare di riutilizzare il concetto di enum.
AR

Risposte:


350

Il valore predefinito per un enum(in effetti, qualsiasi tipo di valore) è 0, anche se questo non è un valore valido per quello enum. Non può essere cambiato.


13
Sostituisci la domanda per chiedere che il valore predefinito per un int sia 42. Pensa a quanto sciocca suona ... La memoria viene oscurata dal runtime prima di ottenerla, gli enumeratori sono strutture che ricordano
ShuggyCoUk

3
@DougS 1. No, ma ammettere di aver sbagliato è. 2. Non ha guadagnato alcuna reputazione dai voti dei commenti.
Aske B.

7
I commenti mi hanno fatto sospettare su questa risposta, quindi l'ho testato e verificato che questa risposta sia sicuramente corretta.
Ian Newson,

1
@JamesCurran Ma quando non utilizzo 0 valori per gli enumerali, incontro "Un'eccezione di tipo 'System.NullReferenceException' si è verificata in MyProjectName.dll ma non è stata gestita nel codice utente. Ulteriori informazioni: Riferimento oggetto non impostato su un'istanza di un oggetto." errore. Come sistemarlo? Nota: utilizzo un metodo di estensione per visualizzare le descrizioni di enum, ma non sono sicuro di come evitare l'errore nel metodo di supporto (GetDescription) definito in questa pagina
Jack

65

Il valore predefinito di qualsiasi enum è zero. Pertanto, se si desidera impostare un enumeratore come valore predefinito, impostare quello su zero e tutti gli altri enumeratori su zero (il primo enumeratore con valore zero sarà il valore predefinito per quell'enum se sono presenti più enumeratori con il valore zero).

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'

Se i tuoi enumeratori non hanno bisogno di valori espliciti, assicurati solo che il primo enumeratore sia quello che desideri sia l'enumeratore predefinito poiché "Per impostazione predefinita, il primo enumeratore ha il valore 0 e il valore di ciascun enumeratore successivo è aumentato di 1." ( Riferimento C # )

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

Orientation o; // initialized to 'None'

38

Se zero non funziona come valore predefinito corretto, è possibile utilizzare il modello di componente per definire una soluzione alternativa per l'enum:

[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);
        }
    }
}

e quindi puoi chiamare:

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

Nota: sarà necessario includere la seguente riga nella parte superiore del file:

using System.ComponentModel;

Ciò non modifica il valore predefinito predefinito del linguaggio C # dell'enum, ma fornisce un modo per indicare (e ottenere) il valore predefinito desiderato.


scusa non funziona per te. Puoi aggiungere più informazioni? è necessario aggiungere la riga utilizzando System.ComponentModel; e quindi scrivere la funzione GetDefaultEnum () in una classe Utilities. Quale versione di .net stai usando?
David,

3
+1 per il riutilizzo dell'attributo del modello di componente e della soluzione pulita. Mi piacerebbe consigliare di cambiare l'ultima riga nella elsedichiarazione come segue: return default(TEnum);. Pugno, ridurrà le Enum.GetValues(...).***chiamate più lente , in secondo luogo (e più importante) sarà generico per qualsiasi tipo, anche non enumerazioni. Inoltre, non si limita il codice da utilizzare solo per gli enum, quindi questo risolverà potenziali eccezioni quando utilizzato su tipi non enum.
Ivaylo Slavov,

1
Questo può essere un uso improprio dell'attributo valore predefinito nel contesto della domanda posta. La domanda posta riguarda il valore inizializzato automaticamente, che non cambierà con la soluzione proposta. Questo attributo è pensato per l'impostazione predefinita di un visual designer che differisce dal valore iniziale. Non viene menzionata la persona che pone la domanda di visual designer.
David Burg,

2
Non c'è davvero nulla nella documentazione MSDN che ComponentModel o DefaultAttribute sia solo una funzionalità di "visual designer". Come afferma la risposta accettata. La risposta breve è "no, impossibile" per ridefinire il valore predefinito di un enum su qualcosa di diverso dal valore 0. Questa risposta è una soluzione alternativa.
David,

17

Il valore predefinito di un enum è qualunque enumerazione equivalga a zero. Non credo che questo sia modificabile da attributi o altri mezzi.

(MSDN dice: "Il valore predefinito di un enum E è il valore prodotto dall'espressione (E) 0.")


13
In senso stretto, non è nemmeno necessario che sia presente un elenco con questo valore. Zero è ancora l'impostazione predefinita.
Marc Gravell

11

Non puoi, ma se vuoi, puoi fare qualche trucco. :)

    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;
    }

utilizzo di questa struttura come semplice enum.
dove è possibile creare pa == Orientation.East (o qualsiasi valore desiderato) per impostazione predefinita
per utilizzare il trucco stesso, è necessario convertire da int per codice.
lì l'implementazione:

        #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

Nel tuo operatore di conversione implicita hai bisogno value + 1, altrimenti ora i tuoi default(Orientation)resi East. Inoltre, rendere privato il costruttore. Buon approccio.
nawfal,

10

Un altro modo che potrebbe essere utile: usare una sorta di "alias". Per esempio:

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

    // Use Status.Default to specify default status in your code. 
    Default = New 
}

1
Il suggerimento è buono, tuttavia, come già menzionato in altre risposte, le enumerazioni hanno il loro valore predefinito pari a zero. Nel tuo caso, l'espressione Status s = default(Status);non sarà uno dei membri dello stato, poiché savrà il valore di (Staus) 0. Quest'ultimo è valido per scrivere direttamente, poiché gli enum possono essere espressi in numeri interi, ma fallirà durante la deserializzazione, poiché il framework assicura che un enum sia sempre deserializzato su un membro valido. Il tuo codice sarà perfettamente valido se cambi la linea New = 10in New = 0.
Ivaylo Slavov,

1
Sì. Nuovo = 0 funzionerebbe. Eviterei personalmente di non stabilire membri enum esplicitamente.
Dmitry Pavlov,

7

In realtà un enumdefault è il primo elemento nel enumcui valore è0 .

Quindi per esempio:

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;

9
Tuttavia, in quel caso "Pony" È "Cat" (ad esempio, Console.WriteLine(Animals.Pony);stampa "Cat")
James Curran,

16
In realtà dovresti: non specificare mai alcun valore O specificare tutti i valori OPPURE specificare solo il valore del primo membro definito. Ecco cosa succede: Cat non ha valore ed è il 1o valore, quindi lo imposta automaticamente su 0. Dog non ha valore, lo imposta automaticamente su PreviousValue + 1 cioè 1. Poney ha un valore, lascia il valore. Quindi Cat = Pony = 0, Dog = 1. Se hai {Cat, Dog, Pony = 0, Frog}, allora 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) ... Ma ho affrontato problemi critici usando enum nei miei progetti e superato facendo qualcosa di seguito ... Quanto mai il mio bisogno era legato al livello di 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
    }

e ottieni il risultato atteso

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

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

Ora cosa succede se il valore viene dal DB .... Ok in questo scenario ... passa il valore nella funzione sottostante e convertirà il valore in enum ... la funzione sottostante ha gestito vari scenari ed è generica ... e credimi è molto veloce ..... :)

    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);
    }

-1 per non rispondere intenzionalmente alla domanda data. Questo non è un ovvio problema di compiti a casa, che dovrebbe essere l'unico caso in cui non rispondi intenzionalmente.
Xynariz,

1
@Xynariz .. scusate ragazzi .. i miei commenti sono stati erroneamente negativi perché la mia vera intenzione era di fornire una soluzione in altro modo ... Quindi ho cambiato i miei commenti ...: D
Moumit

2

Il valore predefinito per un tipo di enumerazione è 0:

  • " Per impostazione predefinita, il primo enumeratore ha il valore 0 e il valore di ciascun enumeratore successivo viene aumentato di 1 ".

  • " Il tipo di valore enum ha il valore prodotto dall'espressione (E) 0, dove E è l'identificatore enum. "

È possibile controllare la documentazione per C # enum qui e la documentazione per la tabella dei valori predefiniti C # qui .


1

Se si definisce l'enum predefinito come l'enum con il valore più piccolo, è possibile utilizzare questo:

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

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

firstEnum == Mine.

Ciò non presuppone che l'enum abbia un valore zero.


0

L'impostazione predefinita è la prima nella definizione. Per esempio:

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

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

Questo tornerà His


haha; Il valore di default è zero. MA! dice, il valore predefinito "zero" sembra essere solo il simbolo predefinito del primo valore denominato; ! quindi in altre parole la lingua sceglie il valore predefinito invece di te; questo accade anche che zero è il valore predefinito di 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;
    }
}

Se si imposta la proprietà su null, verrà restituito Orientations.Nessuno su get. La proprietà _orientation è nulla per impostazione predefinita.


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

Quindi nel codice è possibile utilizzare

public Orientation GetDefaultOrientation()
{
   return default(Orientation);
} 

14
/! \ WRONG /! \ The DefaultValueAttribute in realtà non imposta il valore, è solo a scopo informativo. Quindi sta a te controllare se il tuo enum, field, property, ... ha quell'attributo. PropertyGrid lo utilizza in modo che sia possibile fare clic con il pulsante destro del mouse e selezionare "Ripristina", anche se non si utilizza il valore predefinito, il testo viene visualizzato in grassetto - MA - è comunque necessario impostare manualmente la proprietà sul valore predefinito desiderato.
user276648
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.