If .... wie richtig (auf nothing und Wert) prüfen

  • VB.NET

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

    If .... wie richtig (auf nothing und Wert) prüfen

    Moin!

    ich habe eine Listen-Variable _ListOfHaltungWithHardSplit.

    Nun soll eine If-Abfrage kommen, ob der Count>0 ist.

    Da es aber auch sein kann, dass _ListOfHaltungWithHardSplit ein nothing ist müssten meines Wissens zwei Abfragen kommen.

    Die erste prüft auf das nothing

    VB.NET-Quellcode

    1. If _ListOfHaltungWithHardSplit IsNot Nothing Then


    und dann käme eine Prüfung auf den Count-Wert


    VB.NET-Quellcode

    1. If _ListOfHaltungWithHardSplit.Count>0 Then


    Das würde die gesamte weitere else-Logik aber erheblich erschweren.

    Kann man diese beiden If-Statements irgendwie zusammenfassen?

    Gruß Jan
    AndAlso hilft dir hier.

    Wenn die erste Bedingung nicht zutrifft, wird die 2. erst garnicht mehr geprüft, somit keine Exception wenn Liste is Nothing beim Test ob Count > 0

    VB.NET-Quellcode

    1. If Not _ListOfHaltungWithHardSplit Is Nothing AndAlso _ListOfHaltungWithHardSplit.Count > 0 Then

    Der Unterschied zwischen And und AndAlso/Or und OrElse
    If Not _ListOfHaltungWithHardSplit Is Nothing -> If _ListOfHaltungWithHardSplit IsNot Nothing; das schlägt inzwischen auch VS vor.
    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.
    Nunja, mir zeigte VS diesen Änderungsvorschlag nicht an. Warum ist denn If _ListOfHaltungWithHardSplit IsNot Nothing deiner Meinung nach besser? If finde If Not... besser lesbar, als wie If var IsNot , weil das Not sofort am "Anfang der Bedingung" sichtbar ist. Finde ich dann besonders gut, wenn mehrere Bedingungen in einer Zeile verknüpft sind.
    Tja, Geschmackssache. Ich lager v.a. komplexe Bedingungen in ne Funktion aus, damit ich mir gar nicht den Ausdruck im Gesamten anschauen muss, sondern über den kompakten Funktionsnamen sehe, was da geprüft wird.
    Bilder
    • Not_Is_IsNot.png

      13,92 kB, 447×198, 78 mal angesehen
    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.

    jan99 schrieb:

    Kann man diese beiden If-Statements irgendwie zusammenfassen?

    Takafusa schrieb:

    If Not _ListOfHaltungWithHardSplit Is Nothing AndAlso _ListOfHaltungWithHardSplit.Count > 0 Then
    Da müsste man doch auch einfach mit einem nullbedingten Operator arbeiten können.

    VB.NET-Quellcode

    1. If _ListOfHaltungWithHardSplit?.Count > 0 Then
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    nö, klappt problemlos
    Bilder
    • NullOperatorResult.png

      6,32 kB, 609×198, 55 mal angesehen
    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.
    Vollzitat des direkten Vorposts an dieser Stelle entfernt ~VaporiZed

    Ist kein Bug, steht auch so in der Doku zu Nullable Types in VB.NET. VB konstruiert für die Expression items?.Count > 0 ein Nullable(of Boolean) was halt wegen der Liste, die Null ist, zu Null bzw. [Nothing] evaluiert.
    Aus der Doku:
    AndAlso and OrElse, which use short-circuit evaluation, must evaluate their second operands when the first evaluates to Nothing.

    Und da du in deinem Beispiel auf ein Element der Liste zugreifst, bekommst du die NullReference-Exception.

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

    Schade, dass sie das nu auch noch festgeschrieben haben, anstatt es zu fixen.
    Wie gesagt in c# verhalten sich die entsprechende short-circuit-evaluationen wie man es ich finde erwarten sollte:


    Deine Erklärung verstehe ich nicht.
    Warum muss der zweite AndAlso-Operand ausgewertet werden, wenn der erste bereits Null ergibt? (also mal abgesehen davon, dasses inne Doku steht)
    Wirds in c# ja auch nicht.

    Ich (und c# auch), wir verstehen den AndAlso-Operator als Kurzschreibe dieser Konstruktion:

    VB.NET-Quellcode

    1. (items As List(Of Integer) = Nothing)
    2. If items?.Count > 0 Then If items(0) = 4 Then Nops.Nop()
    3. 'sollte äquivalent sein zu
    4. If items?.Count > 0 AndAlso items(0) = 4 Then Nops.Nop()
    Dass diese Äquivalenz nicht besteht ist imo hanebüchen.
    Und ?. + AndAlso wie gezeigt zu verwenden wäre ja auch sinnig und elegant.

    Wohingegen den zweiten Operanden auszuwerten, obwohl der erste bereits nicht True ergibt - das steht doch im krassen Widerspruch zum Sinn von AndAlso.
    Also zu welchem Zweck ist AndAlso denn erfunden worden? Wenn nicht dazu, den zweiten Operanden auszuwerten sich zu ersparen.

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

    ErfinderDesRades schrieb:

    Wirds in c# ja auch nicht

    Der Compiler in C# macht andere Sachen als der von VB.NET. Das wird deutlicher, wenn man sich folgendes genauer anschaut

    C#-Quellcode

    1. List<int> items = null;
    2. bool b = items?.Count > 0;


    IL:
    Spoiler anzeigen

    Quellcode

    1. ​IL_0000: nop
    2. IL_0001: ldnull
    3. IL_0002: stloc.0
    4. IL_0003: ldloc.0
    5. IL_0004: brtrue.s IL_0009
    6. IL_0006: ldc.i4.0
    7. IL_0007: br.s IL_0012
    8. IL_0009: ldloc.0
    9. IL_000A: call System.Collections.Generic.List<System.Int32>.get_Count
    10. IL_000F: ldc.i4.0
    11. IL_0010: cgt
    12. IL_0012: stloc.1
    13. IL_0013: ret


    VB.NET-Quellcode

    1. ​Dim items As List(Of Integer) = Nothing
    2. Dim b As Boolean? = items?.Count > 0


    IL
    Spoiler anzeigen

    Quellcode

    1. ​IL_0000: nop
    2. IL_0001: ldnull
    3. IL_0002: stloc.0
    4. IL_0003: ldloc.0
    5. IL_0004: brtrue.s IL_0011
    6. IL_0006: ldloca.s 02
    7. IL_0008: initobj System.Nullable<System.Boolean>
    8. IL_000E: ldloc.2
    9. IL_000F: br.s IL_001F
    10. IL_0011: ldloc.0
    11. IL_0012: call System.Collections.Generic.List<System.Int32>.get_Count
    12. IL_0017: ldc.i4.0
    13. IL_0018: cgt
    14. IL_001A: newobj System.Nullable<System.Boolean>..ctor
    15. IL_001F: stloc.1
    16. IL_0020: ret


    In VB.NET hat das Ergebnis aus dem Ausdruck items?.Count > 0 den Datentyp Nullable(Of Boolean) (Man muss sogar den Boolean als Nullable deklarieren, sonst kompiliert das Programm nicht) - C# evaluiert diesen allerdings als Wert und somit false.

    Wenn man sich den IL-Code für folgendes Beispiel genauer anschaut, sieht man, dass intern für den ersten Ausdruck ein Nullable(Of Boolean) erzeugt wird. (IL_001A)

    VB.NET-Quellcode

    1. ​Dim items As List(Of Integer) = Nothing
    2. If items?.Count > 0 AndAlso items(0) > 4 Then
    3. End If


    IL:
    Spoiler anzeigen

    Quellcode

    1. ​IL_0000: nop
    2. IL_0001: ldnull
    3. IL_0002: stloc.0
    4. IL_0003: ldloc.0
    5. IL_0004: brtrue.s IL_0011
    6. IL_0006: ldloca.s 03
    7. IL_0008: initobj System.Nullable<System.Boolean>
    8. IL_000E: ldloc.3
    9. IL_000F: br.s IL_001F
    10. IL_0011: ldloc.0
    11. IL_0012: call System.Collections.Generic.List<System.Int32>.get_Count
    12. IL_0017: ldc.i4.0
    13. IL_0018: cgt
    14. IL_001A: newobj System.Nullable<System.Boolean>..ctor
    15. IL_001F: stloc.2
    16. IL_0020: ldloca.s 02
    17. IL_0022: call System.Nullable<System.Boolean>.get_HasValue
    18. IL_0027: brfalse.s IL_0032
    19. IL_0029: ldloca.s 02
    20. IL_002B: call System.Nullable<System.Boolean>.GetValueOrDefault
    21. IL_0030: brfalse.s IL_0048
    22. IL_0032: ldloc.0
    23. IL_0033: ldc.i4.0
    24. IL_0034: callvirt System.Collections.Generic.List<System.Int32>.get_Item
    25. IL_0039: ldc.i4.4
    26. IL_003A: ble.s IL_0045
    27. IL_003C: ldloca.s 02
    28. IL_003E: call System.Nullable<System.Boolean>.get_HasValue
    29. IL_0043: br.s IL_0046
    30. IL_0045: ldc.i4.0
    31. IL_0046: br.s IL_0049
    32. IL_0048: ldc.i4.0
    33. IL_0049: stloc.1
    34. IL_004A: ldloc.1
    35. IL_004B: brfalse.s IL_004E
    36. IL_004D: nop
    37. IL_004E: nop
    38. IL_004F: ret


    ErfinderDesRades schrieb:

    bereits nicht True ergibt

    Der Ausdruck ist aber auch nicht False, sondern Null. Das wird wahrscheinlich der Grund sein, warum dann der zweite Ausdruck ebenfalls geprüft werden muss.
    Ganz logisch/konsequent ist das von Mikrosaft aber nicht. Weil: Wenn ich in VB ein Nothing mit nem fehlerbehafteten Ausdruck per AndAlso kombiniere, wird's trotzdem ohne Anstalten zu False "short-cicuited". Dementsprechend liegt's nicht an AndAlso/OrElse an sich. Selbst wenn ich die Zielvariable als Boolean? definiere.
    Bilder
    • Result.png

      4,92 kB, 593×103, 34 mal angesehen
    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.
    :D
    Wenn man sich das ebenfalls mal im IL-Code anschaut, erkennt der Compiler, dass Nothing AndAlso ... wahrscheinlich immer False ist und Optimiert das entsprechend.

    VB.NET-Quellcode

    1. Dim items As List(Of Integer) = Nothing
    2. Dim b = Nothing AndAlso items.Count > 0

    IL:
    Spoiler anzeigen

    Quellcode

    1. IL_0000: nop
    2. IL_0001: ldnull
    3. IL_0002: stloc.0
    4. IL_0003: br.s IL_0005
    5. IL_0005: ldc.i4.0
    6. IL_0006: stloc.1
    7. IL_0007: ret


    Während er im oberen Beispiel den Ausdruck wirklich auswerten muss, weil die Liste ja auch nicht Null sein könnte.

    ISliceUrPanties schrieb:

    Der Ausdruck ist aber auch nicht False, sondern Null. Das wird wahrscheinlich der Grund sein, warum dann der zweite Ausdruck ebenfalls geprüft werden muss.
    Bei dreiwertiger Boolscher Algebra - wie sie für Nullable<bool> gültig ist - spielt es keine Rolle, ob Null oder false.
    Beides gilt in 3-wertiger Logik als "Nichtzutreffend" .
    Mein Beispiel aus post#12, mit dem If im If - Then zeigt, dass das auch funktioniert.

    AndAlso aber ist unsinnig implementiert, wenn es bei "Nichtzutreffend" des ersten Operanden den zweiten trotzdem auswertet.

    Hier nochmal ein gesonderter Test des Debakels:

    VB.NET-Quellcode

    1. Public Shared Sub BugMit_AndAlso()
    2. Dim theBool As Boolean? = Nothing
    3. Dim secondOperandExpression As Func(Of Boolean) = Function()
    4. Debug.WriteLine("secondOperand wurde ausgewertet")
    5. Return True
    6. End Function
    7. If theBool Then If secondOperandExpression() Then Dim i = 0
    8. 'sollte äquivalent sein zu
    9. If theBool AndAlso secondOperandExpression() Then Dim i = 0
    10. End Sub


    Und - klar, wenn man IL guckt wird man Unterschiede finden zw. c# und vb.
    Weil in c# funktionierts ja.

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

    ErfinderDesRades schrieb:

    AndAlso aber ist unsinnig implementiert, wenn es bei "Nichtzutreffend" des ersten Operanden...

    Ich würde nicht sagen, dass AndAlso "unsinnig" implementiert ist, sondern es ist ganz klar nach der VB Spezifikation implementiert. Dort steht für "Short-circuiting Logical Operators"
    ​If the first operand in an AndAlso operation evaluates to False or returns True from its IsFalse operator, the expression returns its first operand. Otherwise, the second operand is evaluated and a logical And operation is performed on the two results.

    Und Null ist nunmal nicht False. (Wobei man darüber sichlich gut streiten kann) Folgende Links sind zu dem Thema noch recht interessant: github.com/dotnet/vblang/issues/478 und github.com/dotnet/csharplang/issues/871.

    Das eigentliche Problem für mich ist hier nicht der AndAlso-Operator, sondern, wenn wir bei deinem Beispiel bleiben, dass der VB-Compiler aus items?.Count > 0 ein Nullable(Of Boolean) erzeugt, welches halt null ist.
    Das sehe ich wiederum nicht als das eigentliche Problem. Das ist genau, wie der NullDependent-Operator ?. sinnvollerweise definiert ist, sowohl in c# als auch in vb.
    check ma die b-Datentypen bei:

    VB.NET-Quellcode

    1. dim b = items?.Count > 0
    und

    C#-Quellcode

    1. var b = items?.Count > 0
    Was soll er da auch sonst draus machen, wenn kein Nullable(Of Boolean)?

    Ansonsten sagte Ich ja schon: Das vb-Fehlverhalten wird nicht dadurch richtig, dass sie es in die Dokumentation schreiben.
    Es ist und bleibt Unfug, wenn bei AndAlso der zweite Ausdruck ausgewertet wird, obwohl der erste nicht True ist.

    Wenn sie die Sprache so spezifiziert haben, dann ist eben die Spezifikation auch schon Unfug.

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

    ErfinderDesRades schrieb:

    dann ist eben die Spezifikation auch schon Unfug

    Dann solltest du dich hier github.com/dotnet/vblang/issues mit einem Vorschlag zur Verbesserung von AndAlso und Null einbringen, um die Sprache weiter zu entwickeln :)
    Sei es, wie es ist. Ich denke, das Thema ist dann ja jetzt auch durch. Es wurde genug auf Doku und Spezifikation hingewiesen.