Auswählen des Standardwerts eines Aufzählungstyps, ohne dass Werte geändert werden müssen

208

Ist es in C # möglich, einen Aufzählungstyp mit einem Attribut zu dekorieren oder etwas anderes zu tun, um den Standardwert anzugeben, ohne dass die Werte geändert werden müssen? Die erforderlichen Zahlen können aus irgendeinem Grund in Stein gemeißelt sein, und es wäre praktisch, weiterhin die Kontrolle über die Standardeinstellungen zu haben.

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

Orientation o; // Is 'North' by default.
xyz
quelle
Sollten Sie in der Lage sein, den Standardwert für int?, Double? Usw. zu ändern?
AR
2
@AR Keine Beleidigung für Sie, aber es ist nicht sinnvoll, Aufzählungen mit ints in der Zusammenfassung zu vergleichen , einfach weil sie zufällig als numerische Typen implementiert sind. Wir könnten Aufzählungen als Zeichenfolgen implementieren, und dies würde ihre Nützlichkeit nicht ändern. Ich betrachte die Antwort hier als Einschränkung der Ausdruckskraft der C # -Sprache; Die Einschränkung ist sicherlich nicht in der Idee enthalten, "Werte von einer eingeschränkten Menge zu unterscheiden".
Jpaugh
1
@jpaugh Ich vergleiche keine Aufzählungen mit Ints "in der Zusammenfassung". Ich gebe Beispiele für andere Datentypen (einschließlich Zeichenfolgen und anderer Referenztypen), bei denen das Ändern der Standardeinstellungen keinen Sinn ergibt. Es ist keine Einschränkung, es ist ein Designmerkmal. Konsistenz ist dein Freund :)
AR
@AR Punkt genommen. Ich denke, ich verbringe zu viel Zeit damit, enums als Konstrukte auf Typebene zu betrachten, insbesondere als Summentypen (oder siehe hier . Das heißt, betrachten Sie jede Aufzählung als einen bestimmten Typ, wobei sich alle Werte dieses Typs von den Werten aller anderen Aufzählungen unterscheiden. Aus einer solchen Denkweise heraus ist es für Aufzählungen unmöglich, irgendeinen Wert zu teilen , und tatsächlich fällt diese Abstraktion auseinander, wenn man bedenkt, dass die Standardeinstellung von 0kann für eine gegebene Aufzählung gültig sein kann oder nicht, und ist einfach in jede Aufzählung mit Schuhen besetzt.
Jpaugh
Ja, ich kenne diskriminierte Gewerkschaften. C # -Aufzählungen sind das nicht, daher weiß ich nicht, ob es hilfreich ist, sie auf einem so hohen (und falschen) Niveau zu betrachten. Während ein "Summentyp" sicherlich seine Vorteile hat, hat dies auch eine Aufzählung (vorausgesetzt, es werden geeignete Werte angegeben). zB myColor = Colors.Red | Colors.Blue. Mein Hauptpunkt ist, dass Sie, wenn Sie einen Summentyp für c # möchten, einen erstellen, anstatt zu versuchen, das Konzept einer Aufzählung neu zu verwenden.
AR

Antworten:

349

Der Standardwert für a enum(tatsächlich jeder Werttyp) ist 0 - auch wenn dies kein gültiger Wert dafür ist enum. Es kann nicht geändert werden.

James Curran
quelle
13
Formulieren Sie die Frage neu, um zu fragen, ob der Standardwert für ein int 42 ist. Überlegen Sie, wie dumm das klingt ... Der Speicher wird durch die Laufzeit ausgeblendet, bevor Sie ihn erhalten. Aufzählungen sind Strukturen, an die Sie sich erinnern
ShuggyCoUk
3
@ DougS 1. Nein, aber zuzugeben, dass Sie sich geirrt haben, ist. 2. Er hat sich keinen Ruf durch Kommentare verdient.
Aske B.
7
Die Kommentare haben mich misstrauisch gegenüber dieser Antwort gemacht, also habe ich sie getestet und überprüft, ob diese Antwort definitiv richtig ist.
Ian Newson
1
@JamesCurran Wenn jedoch keine 0-Werte für Aufzählungen verwendet werden, tritt "Eine Ausnahme vom Typ 'System.NullReferenceException' in MyProjectName.dll auf, die jedoch nicht im Benutzercode behandelt wurde. Zusätzliche Informationen: Objektreferenz nicht auf eine Instanz eines Objekts festgelegt." Error. Wie man es repariert? Hinweis: Ich verwende eine Erweiterungsmethode, um die Beschreibungen von enum anzuzeigen, bin mir jedoch nicht sicher, wie ich den Fehler in der auf dieser Seite
Jack
64

Der Standardwert einer Aufzählung ist Null. Wenn Sie also einen Enumerator als Standardwert festlegen möchten, setzen Sie diesen auf Null und alle anderen Enumeratoren auf Nicht-Null (der erste Enumerator mit dem Wert Null ist der Standardwert für diese Aufzählung, wenn mehrere Enumeratoren vorhanden sind mit dem Wert Null).

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'

Wenn Ihre Enumeratoren keine expliziten Werte benötigen, stellen Sie einfach sicher, dass der erste Enumerator derjenige ist, den Sie als Standard-Enumerator verwenden möchten, da "Standardmäßig hat der erste Enumerator den Wert 0 und der Wert jedes aufeinanderfolgenden Enumerators wird um erhöht 1. " ( C # Referenz )

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

Orientation o; // initialized to 'None'
Kaiser XLII
quelle
38

Wenn Null nicht als der richtige Standardwert funktioniert, können Sie mithilfe des Komponentenmodells eine Problemumgehung für die Aufzählung definieren:

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

und dann können Sie anrufen:

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

Hinweis: Sie müssen die folgende Zeile oben in die Datei einfügen:

using System.ComponentModel;

Dies ändert nicht den tatsächlichen Standardwert für die C # -Sprache der Aufzählung, bietet jedoch eine Möglichkeit, den gewünschten Standardwert anzugeben (und abzurufen).

David
quelle
Entschuldigung, es funktioniert nicht für dich. Können Sie weitere Informationen hinzufügen? Sie müssen die Zeile mit System.ComponentModel hinzufügen. und schreiben Sie dann die Funktion GetDefaultEnum () in eine Utilities-Klasse. Welche Version von .net verwenden Sie?
David
3
+1 für die Wiederverwendung des Komponentenmodellattributs und der sauberen Lösung. Ich möchte Ihnen empfehlen, die letzte Zeile in der elseAnweisung wie folgt zu ändern : return default(TEnum);. Erstens werden die langsameren Enum.GetValues(...).***Anrufe reduziert , zweitens (und noch wichtiger) wird es für jeden Typ generisch sein, auch für Nicht-Aufzählungen. Außerdem schränken Sie den Code nicht ein, der nur für Aufzählungen verwendet werden soll, sodass potenzielle Ausnahmen behoben werden, wenn sie für Nicht-Aufzählungstypen verwendet werden.
Ivaylo Slavov
1
Dies kann ein Missbrauch des Standardwertattributs im Kontext der gestellten Frage sein. Die gestellte Frage bezieht sich auf den automatisch initialisierten Wert, der sich mit der vorgeschlagenen Lösung nicht ändert. Dieses Attribut ist für den Standardwert eines visuellen Designers gedacht, der vom Anfangswert abweicht. Es gibt keine Erwähnung durch die Person, die die Frage des visuellen Designers stellt.
David Burg
2
In der MSDN-Dokumentation gibt es wirklich nichts, was das ComponentModel oder DefaultAttribute nur als "visueller Designer" bezeichnet. Wie die akzeptierte Antwort besagt. Die kurze Antwort lautet "Nein, es ist unmöglich", den Standardwert einer Aufzählung auf etwas anderes als den Wert 0 neu zu definieren. Diese Antwort ist eine Problemumgehung.
David
17

Die Standardeinstellung einer Aufzählung ist die Aufzählung, die gleich Null ist. Ich glaube nicht, dass dies durch Attribute oder andere Mittel veränderbar ist.

(MSDN sagt: "Der Standardwert einer Aufzählung E ist der Wert, der durch den Ausdruck (E) 0 erzeugt wird.")

Joe
quelle
13
Streng genommen muss es nicht einmal eine Aufzählung mit diesem Wert geben. Null ist immer noch die Standardeinstellung.
Marc Gravell
11

Sie können nicht, aber wenn Sie wollen, können Sie einen Trick machen. :) :)

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

Verwendung dieser Struktur als einfache Aufzählung.
Wenn Sie pa == Orientation.East (oder einen beliebigen Wert) erstellen können, um
den Trick selbst zu verwenden, müssen Sie von int per Code konvertieren.
dort die Umsetzung:

        #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
quelle
In Ihrem impliziten Konvertierungsoperator benötigen Sie value + 1, ansonsten jetzt Ihre default(Orientation)Retouren East. Machen Sie den Konstruktor auch privat. Guter Ansatz.
Nawfal
10

Eine weitere Möglichkeit, die hilfreich sein könnte - die Verwendung eines "Alias". Beispielsweise:

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

    // Use Status.Default to specify default status in your code. 
    Default = New 
}
Dmitry Pavlov
quelle
1
Der Vorschlag ist gut, aber wie bereits in anderen Antworten erwähnt, haben Aufzählungen einen Standardwert von Null. In Ihrem Fall ist der Ausdruck Status s = default(Status);keines der Statusmitglieder, da ser den Wert von hat (Staus) 0. Letzteres kann direkt geschrieben werden, da Aufzählungen in Ganzzahlen umgewandelt werden können. Bei der Deserialisierung schlägt dies jedoch fehl, da das Framework sicherstellt, dass eine Aufzählung immer an ein gültiges Mitglied deserialisiert wird. Ihr Code ist vollkommen gültig, wenn Sie die Zeile New = 10in ändern New = 0.
Ivaylo Slavov
1
Ja. Neu = 0 würde funktionieren. Ich persönlich würde es vermeiden, Enum-Mitglieder nicht explizit festzulegen.
Dmitry Pavlov
7

Tatsächlich enumist der Standardwert von an das erste Element in enumdessen Wert0 .

Also zum Beispiel:

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
quelle
9
In diesem Fall ist "Pony" jedoch "Cat" (z. B. Console.WriteLine(Animals.Pony);druckt "Cat")
James Curran
16
Eigentlich sollten Sie entweder: niemals einen Wert angeben ODER alle Werte angeben ODER nur den Wert des ersten definierten Elements angeben. Hier passiert Folgendes: Katze hat keinen Wert und es ist der 1. Wert, setzt ihn also automatisch auf 0. Hund hat keinen Wert, setzt ihn automatisch auf PreviousValue + 1, dh 1. Poney hat einen Wert, belassen Sie den Wert. Also Katze = Pony = 0, Hund = 1. Wenn Sie {Katze, Hund, Pony = 0, Frosch} haben, dann ist Hund = Frosch = 1.
user276648
4

The default value of enum is the enummember equal to 0 or the first element(if value is not specified) ... Aber ich hatte kritische Probleme mit der Verwendung von Enum in meinen Projekten und habe es überwunden, indem ich unten etwas getan habe ... Wie auch immer mein Bedarf mit der Klassenstufe zusammenhängt ...

    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
    }

und erhaltenes erwartetes Ergebnis

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

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

Was ist nun, wenn der Wert von der Datenbank kommt? Ok, in diesem Szenario ... Übergeben Sie den Wert in die unten stehende Funktion und es wird der Wert in enum konvertiert ... unter der Funktion werden verschiedene Szenarien behandelt und es ist generisch ... und glauben Sie mir, dass es so ist sehr schnell ..... :)

    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
quelle
-1 für die absichtliche Nichtbeantwortung der gestellten Frage. Dies ist kein offensichtliches Problem bei den Hausaufgaben. Dies sollte der einzige Fall sein, in dem Sie absichtlich nicht antworten.
Xynariz
1
@ Xynariz .. sorry Leute .. meine Kommentare waren fälschlicherweise negativ, weil meine eigentliche Absicht war, eine Lösung auf andere Weise bereitzustellen ... Also habe ich meine Kommentare geändert ...: D
Moumit
2

Der Standardwert für einen Aufzählungstyp ist 0:

  • " Standardmäßig hat der erste Enumerator den Wert 0, und der Wert jedes aufeinanderfolgenden Enumerators wird um 1 erhöht. "

  • " Der Werttyp enum hat den Wert, der durch den Ausdruck (E) 0 erzeugt wird, wobei E die Aufzählungskennung ist. "

Sie können die Dokumentation für C # Enum überprüfen hier , und die Dokumentation für C # Standardwerte Tabelle hier .

Acoelhosantos
quelle
1

Wenn Sie die Standardaufzählung als die Aufzählung mit dem kleinsten Wert definieren, können Sie Folgendes verwenden:

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

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

firstEnum == Mine.

Dies setzt nicht voraus, dass die Aufzählung einen Nullwert hat.

Tal Segal
quelle
0

Der Standardwert ist der erste in der Definition. Beispielsweise:

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

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

Dies wird zurückkehren His

Tony Hopkinson
quelle
Haha; Der "Standardwert" ist Null. ABER! er sagt, der Standardwert "Null" sei nur das Standardsymbol des zuerst genannten Werts; ! Mit anderen Worten, die Sprache wählt anstelle von Ihnen die Standardeinstellung. Dies passiert auch nur, dass Null der Standardwert eines 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;
    }
}

Wenn Sie die Eigenschaft auf null setzen, wird Orientations.None on get zurückgegeben. Die Eigenschaft _orientation ist standardmäßig null.

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

Dann können Sie im Code verwenden

public Orientation GetDefaultOrientation()
{
   return default(Orientation);
} 
Ruslan Urban
quelle
14
/! \ WRONG /! \ Das DefaultValueAttribute legt den Wert nicht fest, sondern dient nur zu Informationszwecken. Dann müssen Sie überprüfen, ob Ihre Aufzählung, Ihr Feld, Ihre Eigenschaft, ... dieses Attribut haben. Das PropertyGrid verwendet es, damit Sie mit der rechten Maustaste klicken und "Zurücksetzen" auswählen können. Auch wenn Sie nicht den Standardwert verwenden, wird der Text fett angezeigt - ABER - Sie müssen Ihre Eigenschaft immer noch manuell auf den gewünschten Standardwert setzen.
user276648