Structure is nothing

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

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von Pascalony.

    Structure is nothing

    Hi,

    ich hab hier ein Problem mit einer klitzekleinen Struktur.

    VB.NET-Quellcode

    1. Public Structure TextEntry
    2. Public entKey As Integer
    3. Public entTime As DateTime
    4. Public entText As String
    5. End Structure
    6. Dim CopyEntry As TextEntry = Nothing


    In diese Struktur schreibe ich mit einer 'Cut" Funktion Daten, die ich später mit 'Paste' wieder einfügen möchte. Das ist natürlich überhaupt kein Problem.

    Aber natürlich kann ich 'Paste' nur aufrufen, wenn ich vorher mit 'Cut' Daten ausgeschnitten habe. Das sollte einleuchtend sein.

    Nun möchte ich halt wissen, ob in 'CopyEntry' etwas drin steht. Deshalb frage ich das in der 'Paste' Prozedur wie folgt ab:

    VB.NET-Quellcode

    1. If CopyEntry <> Nothing Then ...


    Und damit handle ich mir den folgenden Compiler Fehler ein:

    Der <>-Operator ist für die Typen "Module1.TextEntry" und "Module1.TextEntry" nicht definiert.

    Auch 'is nothing' etc. funktionieren nicht.

    Ich weiß, dass sich Strukturen mit 'nothing' irgendwie merkwürdig verhalten. Aber erstens ist mir das Regelwerk nicht so richtig klar. Und zweitens hab ich keine Ahnung, wie ich das nach den Regeln der reinen Lehre lösen sollte !

    Natürlich kann ich das schweinisch mit einem "Flag" lösen ... oder ich könnte ganz auf die Struktur verzichten. Umgehungen gibt es viele. Aber ich bin ja ein wissbegieriger Schüler und würde das gern ordentlich kodieren.

    Kann mir jemand (mit gewohnter Nachsicht) auf die Sprünge helfen?

    LG
    Peter

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Peter329“ ()

    Stichwort Nullable. Damit erweiterst Du eine Structure um die Möglichkeit, sauber Nothing anzunehmen. Denn ein Nullable hat die Eigenschaft HasValue. Die bei Nothing False ist.

    Zur Erklärung, warum Structures und Nothing normalerweise nicht gut zusammenpassen: Structures sind nunmal Wertetypen. Die können normalerweise nicht Nichts sein. So wie z.B. ein Integer. Oder ein Boolean.

    Beispiel:

    VB.NET-Quellcode

    1. Dim CopyEntry As TextEntry? = Nothing

    und später

    VB.NET-Quellcode

    1. If CopyEntry Is Nothing Then
    2. 'und/oder
    3. If CopyEntry.HasValue Then


    (Diesmal habe ich es nicht mit dem Conditional-Operator ? verwechselt :whistling: )
    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 2 mal editiert, zuletzt von „VaporiZed“ ()

    Peter329 schrieb:

    Ich weiß, dass sich Strukturen mit 'nothing' irgendwie merkwürdig verhalten. Aber erstens ist mir das Regelwerk nicht so richtig klar.
    Das täte ich nu denken, ist nu dran, richtig zu lernen, damit dir das klar wird.
    Hast du irgend ein Buch, wo was dazu steht: Structure vs. Class?
    Wenn du das verstanden hast, dann kannste auch VaporiZeds Lösung verstehen (nicht nur abschreiben). Ausserdem gibts noch andere Ansätze, die vlt. sogar einfacher sind.
    Das mit dem "nothing" als Null Wert sauber annehmen, klingt sehr gut.

    Leider kriege ich die Syntax nicht gebacken ! Wenn ich das so kodiere

    VB.NET-Quellcode

    1. Dim CopyEntry As TextEntry? = Nothing


    dann werden alle Assignments abgelehnt:

    VB.NET-Quellcode

    1. CopyEntry.entKey = CInt(dgvText.Item("itmKey", i).Value)


    "entKey" ist kein Member von "Module1.TextEntry?".


    Muss ich die Definition der Struktur anpassen ?

    LG
    Peter


    [edit]

    @EDR eigentlich versuche ich IMMER Lösungen zu verstehen und nicht nur abzuschreiben. Eine Struktur ist sehr viel einfacher als eine Klasse. Es gibt keinen Konstruktor und keine Methoden. Es ist eine reine Zusammenfassung von Objekten. Aber wo ist jetzt dein Punkt? Warum soll das Dingens nicht "nothing" sein können ?
    Um auf die Fields/Structure-Member-Variablen zugreifen zu können: .Value dazwischenschieben CopyEntry.Value.entKey =
    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.

    VaporiZed schrieb:

    CopyEntry.Value.entKey =

    Was dann übrigens keinen Effekt haben würde: Werte in einer List(of) einer private structure ändern
    Es scheint mir, als wäre hier eine Klasse besser geeignet, als eine Structure.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    jau, dass mit dem Value ändert an dem Fehler leider nix. Schade.

    Also ganz offen gestanden, verstehe ich die Sache nicht. Wieso kann ich denn einer Struktur "nothing" zuweisen. Aber hinterher kann ich das nicht abfragen !

    What a brave new world with so many wonders in it ! :)

    Aber gut, ich verstehe schon, dass man das mit einer Klasse dann lösen kann !

    Ich für meinen Teil werde, dass jetzt dadurch lösen, dass ich in den Key -1 schreibe. Der kann sonst nicht vorkommen. Und damit hab ich das Ding vom Hals. Aber EDR hat schon Recht. Verstehen tu ich das nicht.

    Peter329 schrieb:

    Wieso kann ich denn einer Struktur "nothing" zuweisen. Aber hinterher kann ich das nicht abfragen !

    VB ist da etwas... suboptimal. In C# würde das auch nicht funktionieren.

    C#-Quellcode

    1. int a = null; // Funktioniert nicht.
    2. int b = default(int); // Das funktioniert.

    In VB ist Dim b As Integer = Nothing das Äquivalent der zweiten Zeile im C#-Code oben. Alles, was Du in der Structure drin hast, bekommt damit den Standardwert zugewiesen. Bei Integer ist das 0, bei Boolean False, bei Referenztypen ein Null-Pointer, etc.
    Deshalb funktioniert das Zuweisen.

    Aber ein Ausdruck eines Wertetyps (Structures sind Wertetypen) kann nie Nothing sein. Der Grund ist der: Mach mal eine lokale Variable vom Typ Integer.

    VB.NET-Quellcode

    1. Sub Test()
    2. Dim Foo As Integer
    3. End Sub

    Da Integer ein Wertetyp ist, beinhaltet Foo bereits den Wert ansich. Weist Du Foo was anderes zu (Foo = 20), dann wird dieser Wert überschrieben und Foo beinhaltet nun einen anderen Wert.

    Referenztypen (Klassen sind Referenztypen) verhalten sich anders. Mach mal eine lokale Variable vom Typ Button.

    VB.NET-Quellcode

    1. Sub Test()
    2. Dim Bar As Button
    3. End Sub

    Da Button ein Referenztyp ist, beinhaltet Bar nur eine Referenz auf den eigentlichen Wert. Das heißt, die Button-Instanz ist nicht direkt in dieser lokalen Variable drin, sondern die liegt woanders im RAM (auf dem Heap) und Bar beinhaltet nur eine Referenz darauf. Weist Du Bar was anderes zu (Bar = New Button) wird zuerst ein neuer Button irgendwo im RAM abgelegt und Bar bekommt dann die Referenz auf diesen neuen Button. Der alte Button bleibt aber weiterhin bestehen.

    Bar kann aber nicht nur eine Referenz auf einen beliebigen Button haben, sondern auch gar keine Referenz. Das heißt, Bar zeigt nirgends hin. In VB nennt sich dieses Konzept Nothing, in C# null.
    Mit Foo kann das nicht gehen, denn Foo beinhaltet ja direkt den Wert. Bar aber beinhaltet eine Referenz auf den/einen Wert, oder eben garkeine Referenz.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Peter329 schrieb:

    Wieso kann ich denn einer Struktur "nothing" zuweisen.
    Kannst Du nicht, es wird der Default-Wert zugewiesen. Wäre die Variable tatsächlich Nothing, käme hier eine Null-Referenz-Exception:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public Structure TextEntry
    3. Public entKey As Integer
    4. Public entTime As DateTime
    5. Public entText As String
    6. End Structure
    7. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    8. Dim CopyEntry As TextEntry = Nothing
    9. Label1.Text = CopyEntry.entTime.ToString
    10. End Sub
    11. End Class

    In C# geht das nämlich nicht:

    C#-Quellcode

    1. private void button1_Click(object sender, EventArgs e)
    2. {
    3. TextEntry CopyEntry = null; // => TextEntry CopyEntry;
    4. label1.Text = CopyEntry.entTime.ToString();
    5. }
    da kommen diese Fehler:


    =====
    Eine Struktur ist ein Value-Type, wie z.B. eine Integer-Variable.
    Der Gegensatz dazu ist rer Reference-Type, das sind Klassen, die mit New instanziiert werden müssen.
    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!
    Fettnäpfchen Fettfass, here I come. X/
    Möge sich mein Wissen stets erweitern und meine Unwissenheit die anderen (und auch mich selbst) seltener auf falsche Fährten locken.
    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.
    Mein Erklärungs-Versuch: NullableDateTimePicker abschnitt "Strukturen und Klassen".
    Weiss net, ob das klar genug ist - wie gesagt: Ein gut Buch dürfte das am besten behandeln.



    edit: Und auch mein Simpel-Lösungs-Ansatz:
    Du kannst zwar die Structure nicht sinnvoll mit Is Nothing vergleichen - weil eine structure nie Nothing sein kann - aber der String (refType) in der Structure - der ist natürlich durchaus Nothing - solange er nicht gesetzt ist.
    Mit dem Ansatz brauchste also nichtmal mit Nullable etc. anzufange, einfach den String auf Is Nothing testen (nicht: = Nothing!)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Danke, für die Erklärungen. Mir ist das jetzt doch wesentlich klarer geworden.

    Meine Struktur hat drei Elemente von Typ Integer, Date und String.

    Integer und Date sind Basistypen, durchlaufen damit keine Initialisierung wie Referenztypen, sondern sie werden im Speicher einfach angelegt. Dadurch können sie nicht "nothing" werden und haben immer einen Default Wert, in diesem Fall 0 und "low Date".

    Mit String ist das wohl anders. Diese Variablen haben im VB immer eine variable Länge und deswegen kann man da nicht einfach Speicher vorab allokieren. Deshalb ist die String Variable in meiner Struktur zunächst "nothing". Wenn ich die Struktur ohne Zuweisung definiere und dann entText anspreche, erhalte ich einen Null-Verweis.

    Die Zuweisung "structur = nothing" ist dann dahingehend gewöhnungsbedürftig, weil dadurch die Basistypen "Integer" und "Date" auf den Defaultwert zurückgesetzt werden, während bei der String Variablen der Anfangswert (ein Nullstring) gesetzt wird. Damit ist nach der Zuweisung "structure = nothing" die String Variable gerade NICHT mehr "nothing" ! Ein spaßiger Effekt, für den man schon ein wenig Humor braucht ! :)

    Jetzt verstehe ich endlich, was da im VB passiert! Tatsächlich ist das ein bissl irreführend gelöst - C# scheint mir da doch etwas stringenter zu sein.

    Danke an alle, die sich mit meinem Problem befasst haben ! Problem gelöst, Daumen hoch und einen schönen Tag !

    LG
    Peter

    Peter329 schrieb:

    C# scheint mir da doch etwas stringenter zu sein
    Jou, deshalb programmiere ich lieber in C# als in VB.
    Wenn iwas unklar ist, vergleiche ich äquivalenten Code in VB und C#.
    Ist das Ergebnis gleich, ist alles fein.
    Isses unterschiedlich, muss ich halt nachforschen.
    Beispiel:
    Conversion.Val(IRGENDEIN_STRING) ( msdn.microsoft.com/de-de/library/9da280t0(v=vs.110).aspx ) ist nicht so einfach nach C# zu übertragen, wenn man den VisualBasic-Namespace raus haben will, insbesondere, wenn da Grütze im String steht: VB => Wert,
    Convert.ToDouble(...) => Exception.
    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!

    Peter329 schrieb:

    während bei der String Variablen der Anfangswert (ein Nullstring) gesetzt wird. Damit ist nach der Zuweisung "structure = nothing" die String Variable gerade NICHT mehr "nothing" !

    Das kann ich so nicht ganz stehen lassen. In die String-Variable (in .NET wird das hier genaugenommen ein "Feld" genannt) kommt ein Nullverweis rein. Nothing eben. CopyEntry = Nothing könnte man auch so erklären:

    VB.NET-Quellcode

    1. CopyEntry.entKey = Nothing
    2. CopyEntry.entTime = Nothing
    3. CopyEntry.entText = Nothing

    Bei Structures führt das eben dazu, dass der Standardwert rein kommt (Integer und DateTime werden gerne "primitive Datentypen" genannt, sind aber auch nur Structures wie alle anderen Structures auch) und bei Klassen kommt tatsächlich Nothing, also ein Nullverweis, rein.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Niko Ortner Jou.
    @Peter329 In VB werden Strings tatsächlich anders behandelt als in C#, das kommt halt von der VB6-Vergangenheit, wo Strings ValueTypes waren.
    In C# kann man sehr wohl zwischen = null; und = ""; unterscheiden, nicht von ungefähr gibt es die statische Funktion String.IsNullOrEmpty(...).
    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!
    Referenztypen (z.B Klassen) speichern nur eine Referenz auf den Heap, während ein Wertetyp den Wert direkt in der Variable speichert, d.h auf dem Stack wenn ich richtig liege. Ist wie bei einem Array, wo die Variable aufs erste Element im Speicher zeigt.

    Pascalony schrieb:

    d.h auf dem Stack wenn ich richtig liege. Ist wie bei einem Array, wo die Variable aufs erste Element im Speicher zeigt.

    Das ist eher die C++-Herangehensweise. Trifft in .NET nur bedingt zu, denn lokale Variablen müssen nicht zwangsweise auf dem Stack liegen. Der JIT-Compiler kann sich auch entscheiden, dass die in Registern abgelegt werden (wenn genug vorhanden sind) oder sie komplett zu eliminieren, wenn sie z.B. nur einmal beschrieben und gelesen wird und beides direkt hintereinander passiert.
    Dass Array-Variablen auf das erste Element zeigen, ist auch nur bedingt wahr. Der Pointer, der im Hintergrund verwendet wird, zeigt auf einen Header, der vorher im Speicher liegt. Soweit ich das beurteilen kann sind das zwei native ints. Der erste zeigt auf Typeninformationen (damit man zur Laufzeit noch sagen kann, welchen Typ die Elemente eigentlich haben) und der zweite ist die Anzahl an Array-Elementen, wobei da das MSB glaub ich auch noch eine Sonderrolle hat.

    Und natürlich gibt's keine Garantie, dass das bei anderen CLRs ebenfalls so funktioniert. Es ist wahrscheinlich, aber nicht erforderlich.
    Also so direkt sollte man's in .NET nicht sehen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Peter329 schrieb:

    Integer und Date sind Basistypen,
    Stop! Es sind Structure-Typen. "BasisTyp" bezeichnet was ganz anderes.
    durchlaufen damit keine Initialisierung wie Referenztypen, sondern sie werden im Speicher einfach angelegt. Dadurch können sie nicht "nothing" werden und haben immer einen Default Wert, in diesem Fall 0 und "low Date".
    korrekt - jdfs. wenn du mit "low Date" Date.MinValue meinst.
    Mit String ist das wohl anders. Diese Variablen haben im VB immer eine variable Länge und deswegen kann man da nicht einfach Speicher vorab allokieren.
    ist eiglich Sch... egal, warum: String ist halt ein Referenz-Typ - Punkt (aber du hast glaub recht mit deine Vermutung, warum das so ist)
    Deshalb ist die String Variable in meiner Struktur zunächst "nothing". Wenn ich die Struktur ohne Zuweisung definiere und dann entText anspreche, erhalte ich einen Null-Verweis.

    Die Zuweisung "structur = nothing" ist dann dahingehend gewöhnungsbedürftig, weil dadurch die Basistypen "Integer" und "Date" auf den Defaultwert zurückgesetzt werden, während bei der String Variablen der Anfangswert (ein Nullstring) gesetzt wird. Damit ist nach der Zuweisung "structure = nothing" die String Variable gerade NICHT mehr "nothing" !
    Watt?
    "structur = nothing" erzeugt eine neue Structure im Speicher, in deim Fall konkret (CopyEntry) bestehend 4 + 8 + 4 Bytes (Integer, Date, Objekt-Referenz für den String).
    Diese 16 Bytes sind alle mit 0 belegt.
    Wenn bei Integer alle 4 Bytes 0 sind, bedeutet das 0.
    Wenn bei Date alle 8 Bytes 0 sind, bedeutet das 1.1.0001
    Wenn bei String alle 4 Bytes 0 sind bedeutet das Nothing - weil Referenz-Typ.
    Jetzt verstehe ich endlich, was da im VB passiert! Tatsächlich ist das ein bissl irreführend gelöst - C# scheint mir da doch etwas stringenter zu sein.
    Hmm - ich verstehe grad nicht, was du verstehst, aber glaub momentan nicht so rasend wichtig.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Peter329 schrieb:

    Jetzt verstehe ich endlich, was da im VB passiert! Tatsächlich ist das ein bissl irreführend gelöst - C# scheint mir da doch etwas stringenter zu sein.


    Ich verstehe nicht wirklich was du meinst, aber sowohl in C# als auch VB.NET sind Structs Wertetypen. Den Unterschied zwischen Wertetypen und Referenztypen zu verstehen ist wirklich wichtig. Wenn du z.B einen Referenztypen als Parameter übergibst und ihn in einer Methode änderst, ändert sich auch der originale Wert. Bei Wertetypen wird nur eine Kopie übergeben, mit dem Wert in der Variable an sich.

    Edit:

    Niko Ortner schrieb:

    Das ist eher die C++-Herangehensweise.


    Mag sein, aber so hab ichs damals auch bei C# gelesen, aber gut man lernt dazu. In C# scheint Memorymanagment eh nicht zu wichtig mit Garbagecollector etc