Analog zu Enum aber mit Strings? Gibts das?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Analog zu Enum aber mit Strings? Gibts das?

    Hi,

    ich möchte, daß eine Property in meiner Klasse die Werte + und - annehmen kann.

    Dazu hätte ich gerne etwas wie

    VB.NET-Quellcode

    1. Public Enum enCondition
    2. Positive = "+"
    3. Negative = "-"
    4. End Enum
    5. Public Property Condition as enCondition


    Gibts so etwas? Etwas anderes, als wirklich + und - als Strings dort abzulegen, kommt nicht in Frage. Ich lasse Instanzen der Objekte in einer Liste anzeigen und möchte, daß der Nutzer da + und - angezeigt bekommt
    Eine Möglichkeit ist es, ein Dictionary(Of MyEnum, String) zu verwenden.
    Eine Zweite, wäre diese hier.

    Suche doch mal im Netzt nach "enum string" ! ;)
    Klar,
    Du brauchst folgenden Namespace System.ComponentModel.DataAnnotations

    VB.NET-Quellcode

    1. Public Enum Condition
    2. <Display(Name := "+")>
    3. Positive = 0
    4. <Display(Name := "-")>
    5. Negative = 1
    6. End Enum


    Und so kannst du die dann ermitteln

    VB.NET-Quellcode

    1. Dim condition As Condition = Condition.Positive
    2. Dim memberInfo As MemberInfo = GetType(Condition).GetMember(condition.ToString()).First()
    3. Dim displayName As String = memberInfo.GetCustomAttributes(GetType(DisplayAttribute), True).First().Name

    John422 schrieb:

    Ich lasse Instanzen der Objekte in einer Liste anzeigen und möchte, daß der Nutzer da + und - angezeigt bekommt
    Man könnte einfach die .ToString() Methode der Klasse überschreiben.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Ich bin vor einiger Zeit einen ähnlichen Weg gegangen, verwende aber aus einem Grund nicht das Display-Attribut, sondern das Description-Attribut: Ich kann den Spaß an ne ListBox binden*. Daraus hab ich mir ein Paket gemacht, was die ListBox-Handhabung stark vereinfacht:
    Hilfsklasse

    VB.NET-Quellcode

    1. Imports System.ComponentModel
    2. Friend NotInheritable Class EnumDescriptionListCreator
    3. Public Shared Function CreateList(Of T)() As IEnumerable(Of EnumDescriptionItem(Of T))
    4. Dim Values = [Enum].GetValues(GetType(T)).Cast(Of T)
    5. If Values.Any(Function(x) Attribute.GetCustomAttribute(x.GetType.GetField(x.ToString), GetType(DescriptionAttribute)) Is Nothing) Then Throw New ArgumentException("Nicht alle Werte des Enums haben ein Description-Attribut.")
    6. If Values.Select(Function(x) Attribute.GetCustomAttribute(x.GetType.GetField(x.ToString), GetType(DescriptionAttribute))).Distinct.Count < Values.Count Then Throw New ArgumentException("Jeder Description-Attributwert innerhalb des Enums muss einmalig sein.")
    7. Dim RawList As New List(Of (Description As String, Value As T))
    8. RawList.AddRange(Values.Select(Function(x) (DirectCast(Attribute.GetCustomAttribute(x.GetType.GetField(x.ToString), GetType(DescriptionAttribute)), DescriptionAttribute).Description, x)))
    9. Dim EnumDesciptionList As New List(Of EnumDescriptionItem(Of T))
    10. RawList.ForEach(Sub(x) EnumDesciptionList.Add(New EnumDescriptionItem(Of T) With {.Value = x.Value, .Description = x.Description}))
    11. Return EnumDesciptionList
    12. End Function
    13. End Class
    14. Public Class EnumDescriptionItem(Of T)
    15. Property Value As T
    16. Property Description As String
    17. End Class



    Extensions

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Friend Module Extensions
    3. <Extension> Public Sub BindToEnum(Of T)(ListBox As ListBox)
    4. ListBox.DataSource = EnumDescriptionListCreator.CreateList(Of T)
    5. ListBox.DisplayMember = "Description"
    6. ListBox.ValueMember = "Value"
    7. End Sub
    8. <Extension> Public Sub SetEnumItem(Of T As {Structure, IComparable})(ListBox As ListBox, ActualItem As T)
    9. If ListBox.Items.Count = 0 Then Return
    10. ListBox.SelectedItem = ListBox.Items.Cast(Of EnumDescriptionItem(Of T)).Single(Function(x) x.Value.CompareTo(ActualItem) = 0)
    11. End Sub
    12. <Extension> Public Function GetSelectedEnumValue(Of T As {Structure, IComparable})(ListBox As ListBox) As T
    13. If ListBox.SelectedItem Is Nothing Then Throw New ArgumentException("Es wurde kein ListBox-Eintrag ausgewählt." & If(ListBox.Items.Count = 0, " Die ListBox ist leer.", String.Empty))
    14. Return DirectCast([Enum].GetValues(GetType(T)), T()).Single(Function(x) x.CompareTo(DirectCast(ListBox.SelectedItem, EnumDescriptionItem(Of T)).Value) = 0)
    15. End Function
    16. End Module



    VB.NET-Quellcode

    1. Imports System.ComponentModel
    2. Public Class FrmMain
    3. Private Sub FrmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. LbxSpecies.BindToEnum(Of Species)
    5. End Sub
    6. Private Sub BtnShowSelectedSpecies_Click(sender As Object, e As EventArgs) Handles BtnShowSelectedSpecies.Click
    7. LblSelectedSpecies.Text = LbxSpecies.GetSelectedEnumValue(Of Species).ToString
    8. End Sub
    9. Private Sub BtnSelectMammals_Click(sender As Object, e As EventArgs) Handles BtnSelectMammals.Click
    10. LbxSpecies.SetEnumItem(Species.Mammals)
    11. End Sub
    12. End Class
    13. Friend Enum Species
    14. <Description("Insekten")> Insects
    15. <Description("Spinnen")> Spiders
    16. <Description("Säugetiere")> Mammals
    17. End Enum


    (nachdem die Säugetiere per Button gewählt wurden, ändert sich natürlich erstmal das Label oben rechts nicht, bis man wieder den anderen Button drückt).
    Das hatte mir den Umgang schon mal sehr erleichtert, da ich nun im Code mit den eigentlichen Enum-Werten arbeiten kann, auf dem GUI aber die Description-Texte sehe.

    Problematisch war nun noch der Umgang mit einem DGV. Ich habe eine Lösung heute morgen zusammengebaut, angetrieben durch diesen Thread. Dazu die Beispielklasse

    VB.NET-Quellcode

    1. Friend Class Animal
    2. Property Name As String
    3. Property Species As Species
    4. End Class

    Bindet man diese bzw. eine List(Of) davon an eine BindingSource und diese an ein DGV, bekommt man erstmal nur die Values angezeigt.

    Stellt man die SpeciesColumn aber auf Typ ComboBoxColumn (und lässt die Einstellungen DisplayMember, ValueMember, DataSource weg), kann man auch dort das Enum codeseitig binden:

    VB.NET-Quellcode

    1. Dim SpeciesColumn = DirectCast(DataGridView1.Columns(1), DataGridViewComboBoxColumn)
    2. SpeciesColumn.DataSource = EnumDescriptionListCreator.CreateList(Of Species)
    3. SpeciesColumn.ValueMember = "Value"
    4. SpeciesColumn.DisplayMember = "Description"



    ##########

    * ok, wenn man es richtig macht, geht es natürlich auch mit dem DisplayAttribute … :S
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „VaporiZed“ ()

    @John422 Ein wenig musst Du aufpassen:
    Beim Enum sorgt die IDE / der Compiler dafür, dass jeder Eintrag genau ein Mal auftreten kann.
    Wenn das für Deine Strings ebenso gelten soll, musst Du das explizit implementieren.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!