none
Array in Strukturvariable RRS feed

  • Frage

  • Hallo Experten,

    auf Tip von Euch habe ich eine Strukturvariable (Fragen) definiert. Diese Struktur enthält die Variable Antworten, welche ein Array darstellt. Da die größe des Arrays innerhalb der Strukturdefinition nicht festgelegt werden kann (warum auch immer) muss diese später (denke ich erfolgen) im Code gemacht werden. Der folgende Code erzeugt entsprechen einen Fehler sobald ich der Variable Fragen(index).Antworten(index) ein Wert zuweise.

    Public Structure Fragen
            Public Verwendet As Boolean
            Public Bereich As String
            Public SGrad As String
            Public Frage As String
            Public Antworten() As String
            Public RAntwort As Int16
    End Structure
    
    Public Sub read()
            Dim i As Int16=1
            Dim xFragen(35) as Fragen
            xFragen(i).Verwendet = False
            xFragen(i).Bereich = "X1"
            xFragen(i).SGrad =  "X2"
            xFragen(i).Frage =  "X3"
            xFragen(i).Antworten(0) = "Hier ist der Fehler"      
            ....          
    end Sub
    

    Wo und wie kann ich das Array von ...Antworten auf z.B. 3 festlegen.

    Vielen Dank mfg eem monarch

    Mittwoch, 18. Januar 2012 16:18

Antworten

  • Hallo,

    da ändert sich (fast) gar nichts.
    Ergänze den Code (bzw. ändere Testen):

        Public Sub Read()
            Read(Path.Combine(Application.StartupPath, "Fragen.dat"))
        End Sub
    
        Public Sub Read(ByVal fileName As String)
            If Not File.Exists(fileName) Then
                Return
            End If
            ' .NET 4
            For Each eingabe In File.ReadLines(fileName)
                Dim felder = eingabe.Split("|"c)
                Dim frage = New Frage() With
                {
                    .Bereich = felder(1),
                    .SGrad = felder(2),
                    .Frage = felder(3),
                    .Antworten = New List(Of String) From {felder(4), felder(5), felder(6), felder(7)},
                    .RAntwort = Integer.Parse(felder(8))
                }
                Me.Add(Frage)
            Next
        End Sub
    
        ' Fürs Testen...
        Friend Shared Sub Test()
            'Dim alleFragen = Fragen.Create()
            Dim alleFragen = New Fragen()
            alleFragen.Read()
            For Each f In alleFragen
                Console.WriteLine("Frage: {0}, Antworten: {1}", f.Frage, f.Antworten.Count)
                For Each a In f.Antworten
                    Console.WriteLine("    Antwort: " & a)
                Next
            Next
        End Sub
    
    


    wobei ich einen andere Pfadangabe verwendet habe, mit dem Input:

    |X0|X1|Struktur oder Klasse|Struktur|Klasse|Keines von beiden||1
    |X1|X2|Array oder Auflistung|Array|Auflistung|||1
    |X2|X3|Frage ohne Antwort|1. Antwort|2. Antwort|3. Antwort|4. Antwort|5
    |X2|X4|Frage ohne Antwort|||||0

    Willst Du die Löcher (fehlende Antworten) loswerden, wirf

    .Antworten = New List(Of String) From {felder(4), felder(5), felder(6), felder(7)},
    

    oben aus dem Konstruktor raus und füge nur die nicht-leeren hinzu, z. B.

                For Each antwort In {felder(4), felder(5), felder(6), felder(7)}
                    If Not String.IsNullOrEmpty(antwort) Then
                        frage.Antworten.Add(antwort)
                    End If
                Next
    
    

    Gruß Elmar

    • Als Antwort markiert Monarch-Falter Donnerstag, 19. Januar 2012 16:42
    Donnerstag, 19. Januar 2012 15:27
    Beantworter

Alle Antworten

  • Hallo,

    soweit ich weiß, kannst Du innerhalb Deiner Structure auch eine Sub New() definieren. Hier kannst Du dann Dein Array initialisieren.

    Ohne Gewähr. Ich kanns an meinem Handy nicht testen :-)


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Mittwoch, 18. Januar 2012 16:51
  • hmmm und wie soll mir das helfen ????
    Mittwoch, 18. Januar 2012 17:04
  • Hallo,

    irgendwie kommt mir das bekannt vor: Structure Variable mit Array
    was wohl im Weihnachtstrubel untergegangen ist...

    Schon dort gesagt: Verwende eine Klasse (Class).

    Strukturen sind für leicht gewichtige (und oft kurzlebige) Objekte gedacht.
    Und damit sie das sein können, erfolgt das Erzeugen direkt durch die CLR,
    die alle Variablen mit dem Standardwert (0) vorbelegt.
    Deswegen ist dort auch kein Standardkonstruktor möglich - der würde gar nicht verwendet.

    Das ist aber nur einer der Gründe warum man keine Struktur verwenden sollte.

    Unabhängig von Struktur oder Klasse:

    Antworten() As String

    ist zunächst die Deklaration (Platzhalter) einer Array-Variablen, und es wird damit noch kein Array erzeugt.
    Dies würde erst durch new geschehen, z. B.:

    Antworten = new String(2) {} ' Ein leeres Array für 3 Strings
    ' oder auch
    Antworten =  { "1. Antwort", "2. Antwort", "3. Antwort" }
    weitere Varianten: Gewusst wie: Initialisieren von Arrayvariablen in Visual Basic

    Ein Nachteil von Arrays ist aber, dass sie in ihrer Größe fest dimensioniert sind.
    Ändert sich die Größe im Nachhinein (hier: kommen Antworten hinzu oder fallen weg),
    so muss ein neues Array erzeugt werden und die Inhalte übertragen werden.
    (Die Redim Anweisung von Visual Basic tut auch nichts anderes.)

    Auch alle anderen Optionen, z. B. Verschieben oder Einfügen einer Antwort, sind bei Arrays
    mit relativ mehr Aufwand verbunden.

    Deswegen hatte ich Dir schon damals als Alternative die List(T) empfohlen.
    Dort ist werkelt im Hintergrund auch ein Array. Aber die Operationen fürs
    Anfügen (Add), Einfügen (Insert), Löschen (Remove, RemoveAt) usw. sind
    dort schon ausprogrammiert.

    Und was das Initialiseren in Read angeht, weisst Du ja seit gestern wie Du es
    direkter hinbekommen kannst.

    Und machst Du aus den Antworten zudem eine Standard Eigenschaft,
    so kannst Du sie (fast) genauso verwenden wie ein Array.

    Gruß Elmar

    Mittwoch, 18. Januar 2012 21:01
    Beantworter
  • Servus Elmar,

    nein ist nicht untergegangen , also das List(T) verwende ich , was ja auch von Vorteil ist. Wie ich aber folgende Anforderung in ein List(T) bekommen bzw. verwalten soll ist mir unklar.

    1) Wie ich festgestellt habe ist eine Festlegung der Dimension innerhalb der Strukturdefinition (Public Structure Fragen ... end Structure) grundsätzlich nicht möglich.

    2) Wenn ich ein Array z.B xFragen(35) as Fragen definiere, ist die Dimension von Antworten immer noch null. Diese muss über Redim erzeugt werden, wie jetzt festgestellt habe. Das hat Vorteil in meinen Fall da ich je xfragen(x) die Dimension für Antworten einzeln festlegen kann. z.B. redim xFragen(1).Antworten(4) und redim xfragen(3).Antworten(6).

    3) Ich habe je nach Configuration(durch User) Controls erzeugt, welche im einer List(of T) zusammengefast sind. Über den Index (item) läst sich nun recht einfach eine Beziehung zu der Strukturvariable aufbauen.

    Da die Wertinhalte der Struktur statisch ist und nur innerhalb einer Form gebraucht werden, (was ja auch nichts anderes als eine Klasse ist, soweit ich das verstanden habe), ist doch eine Variablenstruktur ein guter Weg oder ?

    mfg eem monarch

     P.S: der folgende Code von gestern habe ich so vor dir auch übernommen

        Dim labelList As List(Of Label)
    
        Public Sub New()
            InitializeComponent()
    
            labelList = New List(Of Control) From
                {
                    Me.Label1,
                    Me.Label2
                }
        End Sub
    
    

     

     


    Donnerstag, 19. Januar 2012 10:51
  • So, ich nochmal.

    Ich würde auch ein Klasse bevorzugen, aber ich dachte wenn Du schon nach einer Structure frägst....

     


    hmmm und wie soll mir das helfen ????


    'Visual Basic 2008 - .net 3.5 - Any CPU
    Public Structure Fragen
        Public Verwendet As Boolean
        Public Bereich As String
        Public SGrad As String
        Public Frage As String
        Public Antworten() As String
        Public RAntwort As Int16
        Public Sub New(ByVal AnzahlDerAntworten As Integer)
            Antworten = New String (AnzahlDerAntworten - 1) {}
        End Sub
    End Structure
    


    'Visual Basic 2008 - .net 3.5 - Any CPU
        Public Sub read()
            Dim i As Int16 = 1
            Dim xFragen(35) As Fragen
    
            xFragen(i) = New Fragen(5)
    
            xFragen(i).Verwendet = False
            xFragen(i).Bereich = "X1"
            xFragen(i).SGrad = "X2"
            xFragen(i).Frage = "X3"
            xFragen(i).Antworten(0) = "Hier ist kein Fehler mehr"
    
        End Sub
    


    übrigens: Arrays in VB.NET sind immer 0 basierend. Also Dein Array xFragen(35) geht von xFragen(0)...xFragen(35)
    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/

    • Bearbeitet Heslacher Donnerstag, 19. Januar 2012 16:14 Elmar beglückt
    Donnerstag, 19. Januar 2012 12:24
  • Hallo,

    zu 1)
    Das liegt am bereits beschriebenen Verhalten. Es wird bei Strukturen kein Konstruktur ausgeführt,
    ergo kann auch kein Array erzeugt werden (das braucht wiederum ein new, wie ausgeführt).

    Und wie gesagt: Wenn Du keinen guten Grund hast eine Structure zu verwenden, ist Class die richtige Wahl.
    Hier sprechen sogar konkrete Gründe dagegen:
    Nur Klassen können für die Datenbindung verwendet werden (die braucht u. a. auch Eigenschaften,
    deswegen werden unten ebensolche verwendet)
    Aber auch andere Framework Komponenten, z. B. das Entity Framework für die Datenspeicherung erfordern eine Klasse.
    Mit der Wahl als Structure schliesst Du die Verwendung von vornherein aus, ohne einen Mehrwert zu haben.

    zu 2)
    Auf Redim solltest Du verzichten, das stammt aus einer anderen Zeit (VB Classic) und produziert nur reichlich Overhead
    (und ebenfalls ohne Mehrwert).

    Ein interpolierter Aufbau würde könnte z. B. so aussehen:

    Public Class Frage
        Public Property Verwendet As Boolean
        Public Property Bereich As String
        Public Property SGrad As String
        Public Property Frage As String
    
        Public Property Antworten As New List(Of String)
    
        Public Property RAntwort As Integer
    End Class
    
    Public Class Fragen
        Inherits List(Of Frage)
    
        Public Shared Function Create() As Fragen
            Dim liste = New Fragen() From
            {
                New Frage With
                {
                    .Bereich = "X1", .SGrad = "X2", .Frage = "Struktur oder Klasse", .RAntwort = 1,
                    .Antworten = New List(Of String) From {"Struktur", "Klasse", "Keines von beiden"}
                },
                New Frage With
                {
                    .Bereich = "X1", .SGrad = "X2", .Frage = "Array oder Auflistung", .RAntwort = 1,
                    .Antworten = New List(Of String) From {"Array", "Auflistung", "Keines von beiden"}
                }
            }
            ' Beispiel für dynamische Erzeugung
            Dim nochneFrage = New Frage With {.Bereich = "X1", .SGrad = "X2", .Frage = "Noch eine Frage"}
            nochneFrage.Antworten.Add("1. Antwort")
            nochneFrage.Antworten.Add("2. Antwort")
            liste.Add(nochneFrage)
    
            Dim weitereFrage = New Frage With {.Bereich = "X1", .SGrad = "X2", .Frage = "Eine dritte Frage"}
            weitereFrage.Antworten = New List(Of String) From {"Keine Antwort", "Eine Antwort"}
            weitereFrage.Antworten.Add("Weiss ich nicht.")
            liste.Add(weitereFrage)
    
            Dim ohneAntwort = New Frage With {.Bereich = "X1", .SGrad = "X2", .Frage = "Eine Frage ohne Antworten"}
            liste.Add(ohneAntwort)
    
            Return liste
        End Function
    
        ' Fürs Testen...
        Friend Shared Sub Test()
            Dim alleFragen = Fragen.Create()
            For Each f In alleFragen
                Console.WriteLine("Frage: {0}, Antworten: {1}", f.Frage, f.Antworten.Count)
                For Each a In f.Antworten
                    Console.WriteLine("    Antwort: " & a)
                Next
            Next
        End Sub
    End Class
    
    
    


    Ich habe die Fragen zu einer Auflistung von Frage gemacht, die von List(T) erbt.

    So ist eine Frage eine eigenständige Klasse.
    Und könnte (als reine Zukunftsmusik) um INotifyPropertyChanged ergänzt werden,
    so dass man  die Datenbindung von Windows Forms nutzen könnte.

    Aus Read habe ich Create gemacht, weil es besser die Funktion beschreibt.
    Dort sind einige Varianten fürs statische wie dynamische Erzeugen gezeigt.
    Mit Fragen.Test kannst Du das Ergebnis davon sehen.

    Wenn Du später weitere Funktionen hast, die die gesamte Auflistung (alle Fragen) betreffen,
    wären sie in Fragen besser aufgehoben.
    Funktionen die wiederum nur bei einer Frage benötigt werden gehören dorthin.

    Gruß Elmar

    Donnerstag, 19. Januar 2012 12:38
    Beantworter
  • Servus Elmar,

    danke für deine Geduld, das werde ich testen ... und dabei was lernen, zur zeit sieht das bei mir noch so aus ...

    Die Daten werden nur gelesen und sind statisch, also ohne Änderungen

    Public Structure Fragen
            Public Verwendet As Boolean
            Public Bereich As String
            Public SGrad As Int16
            Public Frage As String
            Public Antworten() As String
            Public RAntwort As Int16
        End Structure
        Public xFragen(35) As Fragen
        Public Sub read()
            Dim i As Int16 = 0
    
    
    
            Dim myFileInfo As New IO.FileInfo(System.AppDomain.CurrentDomain.BaseDirectory & "Data\Quiz_Fragen.dat")
            ' Nun schauen wir nach ob eine Textdatei existiert
            If Not myFileInfo.Exists Then
                ' Wenn nicht, Meldung
                Beep()
            End If
            ' Erst benötigen wir eine Variable die den Text aufnimmt
            Dim textline As String
            ' Nun über StreamReader einlesen (wir verwenden Using, das räumt sich selbst auf)
            Using lese As New System.IO.StreamReader(System.AppDomain.CurrentDomain.BaseDirectory & "Data\Quiz_Fragen.dat", System.Text.Encoding.Default)
                Do While Not lese.EndOfStream
                    textline = lese.ReadLine
                    Dim sArray() As String
                    ReDim xFragen(i).Antworten(3)
                    sArray = Split(textline, "|")
                    xFragen(i).Verwendet = False
                    xFragen(i).Bereich = sArray(1)
                    xFragen(i).SGrad = sArray(2)
                    xFragen(i).Frage = sArray(3)
                    xFragen(i).Antworten(0) = sArray(4)
                    xFragen(i).Antworten(1) = sArray(5)
                    xFragen(i).Antworten(2) = sArray(6)
                    xFragen(i).Antworten(3) = sArray(7)
                    xFragen(i).RAntwort = sArray(8)
                    i = i + 1
                Loop
            End Using
        End Sub
    


    ich sag mal danke .... mfg eem monarch

    Donnerstag, 19. Januar 2012 13:39
  • Hallo,

    da ändert sich (fast) gar nichts.
    Ergänze den Code (bzw. ändere Testen):

        Public Sub Read()
            Read(Path.Combine(Application.StartupPath, "Fragen.dat"))
        End Sub
    
        Public Sub Read(ByVal fileName As String)
            If Not File.Exists(fileName) Then
                Return
            End If
            ' .NET 4
            For Each eingabe In File.ReadLines(fileName)
                Dim felder = eingabe.Split("|"c)
                Dim frage = New Frage() With
                {
                    .Bereich = felder(1),
                    .SGrad = felder(2),
                    .Frage = felder(3),
                    .Antworten = New List(Of String) From {felder(4), felder(5), felder(6), felder(7)},
                    .RAntwort = Integer.Parse(felder(8))
                }
                Me.Add(Frage)
            Next
        End Sub
    
        ' Fürs Testen...
        Friend Shared Sub Test()
            'Dim alleFragen = Fragen.Create()
            Dim alleFragen = New Fragen()
            alleFragen.Read()
            For Each f In alleFragen
                Console.WriteLine("Frage: {0}, Antworten: {1}", f.Frage, f.Antworten.Count)
                For Each a In f.Antworten
                    Console.WriteLine("    Antwort: " & a)
                Next
            Next
        End Sub
    
    


    wobei ich einen andere Pfadangabe verwendet habe, mit dem Input:

    |X0|X1|Struktur oder Klasse|Struktur|Klasse|Keines von beiden||1
    |X1|X2|Array oder Auflistung|Array|Auflistung|||1
    |X2|X3|Frage ohne Antwort|1. Antwort|2. Antwort|3. Antwort|4. Antwort|5
    |X2|X4|Frage ohne Antwort|||||0

    Willst Du die Löcher (fehlende Antworten) loswerden, wirf

    .Antworten = New List(Of String) From {felder(4), felder(5), felder(6), felder(7)},
    

    oben aus dem Konstruktor raus und füge nur die nicht-leeren hinzu, z. B.

                For Each antwort In {felder(4), felder(5), felder(6), felder(7)}
                    If Not String.IsNullOrEmpty(antwort) Then
                        frage.Antworten.Add(antwort)
                    End If
                Next
    
    

    Gruß Elmar

    • Als Antwort markiert Monarch-Falter Donnerstag, 19. Januar 2012 16:42
    Donnerstag, 19. Januar 2012 15:27
    Beantworter
  • Hallo Hannes,

    Du würdest mich glücklich machen ;-), wenn Du Redim kippen würdest:

            Public Sub New(ByVal AnzahlDerAntworten As Integer)
                Antworten = New String(AnzahlDerAntworten - 1) {}
            End Sub
    
    

    Gruß Elmar

     

    Donnerstag, 19. Januar 2012 15:42
    Beantworter
  • Hallo Elmar,

    das habe ich doch gerne gemacht.

    Gruß

    Hannes


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Donnerstag, 19. Januar 2012 16:15
  • Danke,

    habe ich jetzt so gemacht und funzt prima .... danke

    mfg eem monarch

     

    Donnerstag, 19. Januar 2012 16:42