Vorhandener Widening Operator CType von String zu Structure stört implizite Typenkonvertierung von Ganzzahlliteralen

  • VB.NET

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von hal2000.

    Vorhandener Widening Operator CType von String zu Structure stört implizite Typenkonvertierung von Ganzzahlliteralen

    Vorwort:

    VB.NET-Quellcode

    1. Dim A As Short = 123S
    2. Dim B = 123S
    3. Dim C As Integer = 123
    4. Dim D = 123
    5. Dim E As Long = 123L
    6. Dim F = 123L
    7. Dim G As Integer = 123S
    8. Dim H As Integer = 123L

    A hat den Typ Short. 123S hat den Typ Short. (Das Suffix S steht für "Short".)
    B hat den Typ Short, weil der Compiler den Typ Short von 123S übernimmt. (Vorausgesetzt Option Infer On.)
    C hat den Typ Integer. 123 hat den Typ Integer. (Wenn kein Suffix, wird Integer angenommen, außer es passt nicht in Integer sondern nur noch in Long, dann wird automatisch Long verwendet.)
    D hat den Typ Integer, weil der Compiler den Typ Integer von 123 übernimmt.
    E hat den Typ Long. 123L hat den Typ Long. (Das Suffix L steht für "Long".)
    F hat den Typ Long, weil der Compiler den Typ Long von 123L übernimmt.
    G hat den Typ Integer. 123S hat den Typ Short. Ein Wert vom Typ Short passt immer ohne Informationsverlust in eine Variable vom Typ Integer, deshalb erlaubt der Compiler die implizite Konvertierung.
    H hat den Typ Integer. 123L hat den Typ Long. Nicht jeder Wert vom Typ Long passt ohne Informationsverlust in eine Variable vom Typ Integer, deshalb erlaubt der Compiler im Normalfall keine implizite Konvertierung, aber da es sich um eine Konstante handelt und der Compiler sicherstellen kann, dass dieser Wert vom Typ Long, nämlich 123L, auch in Integer passt, wird die implizite Konvertierung trotzdem erlaubt. Das ist praktisch, wenn man z.B. eine Methode aufrufen will, die Byte entgegennimmt, dann muss man nicht Foo(CByte(123)) schreiben.

    Man behalte sich für das nachfolgende Problem den Fall H im Hinterkopf.

    Folgender Code:

    VB.NET-Quellcode

    1. Public Shared Sub Main(Args As String())
    2. Dim I As Foo
    3. I = 123
    4. I = 123S
    5. I = 123L
    6. 'I = "123"
    7. End Sub
    8. Public Structure Foo
    9. Public Value As Integer
    10. 'Public Shared Widening Operator CType(Value As String) As Foo
    11. ' Return New Foo With {.Value = Integer.Parse(Value)}
    12. 'End Operator
    13. Public Shared Widening Operator CType(Value As Integer) As Foo
    14. Return New Foo With {.Value = Value}
    15. End Operator
    16. End Structure


    Zeile 3 funktioniert wie man es erwartet. Der Operator CType in Zeile 17 wird aufgerufen, 123 wird als Integer übergeben, zurück kommt ein Foo mit Value = 123.
    Zeile 4 funktioniert aufgrund von Fall G oben: Der Operator CType in Zeile 17 nimmt einen Integer entgegen, Short passt immer in Integer, also erlaubt der Compiler die implizite Konvertierung.
    Zeile 5 funktioniert aufgrund von Fall H oben: Long passt nicht immer in Integer, aber der Compiler weiß, dass die Konstante 123L in Integer passt und erlaubt deshalb die implizite Konvertierung.


    Bisher macht alles Sinn!


    Ent-kommentiert man allerdings Zeilen 6 und 13 bis 15, ändert sich die Situation unerwartet:
    Zeile 6 funktioniert wie erwartet, weil der Operator CType in Zeile 13 aufgerufen wird.
    Aber plötzlich funktioniert Zeile 5, also Fall H nicht mehr! Der Wert vom Typ "Long" kann nicht in "Foo" konvertiert werden..

    Das Problem tritt in Visual Basic 2010 und 2017 auf.

    Gibt es dafür einen guten Grund oder handelt es sich um einen Bug im Compiler oder vielleicht sogar ein Problem in der Sprache ansich? Mir fällt zumindest keine Situation ein, in der das Vorhandensein eines Operator CType von String zum anderen Typ irgendwelche Probleme mit den impliziten Konvertierungen machen könnte.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Jo - vb ist immer noch vergleichsweise offen für Schmuddel-Code.
    Weil imo ist das mit dem H nicht sonderlich sauber - probier das mal in c#.
    Habs jetzt selbst probiert - es geht in c# nicht.

    Jo, und dann scheinen sie die Schmuddelei nicht konsequent zuende geführt zu haben, sodass sich der Compiler nu inkonsistent verhält: Bei nur einer Methode lässt er 5e grade sein, bei Überladung weiss er dann aber doch nicht, welche er nehmen soll - und nimmt dann scheints keine.
    @Niko Ortner Bei einem Operator mag das gehen, bei zweien eben nicht mehr.
    Sobald Du einen weiteren passenden Operator hinzufügst, redet die Nachbarin wieder mit Dir:

    VB.NET-Quellcode

    1. Public Shared Widening Operator CType(Value As Long) As Foo
    2. Return New Foo With {.Value = CInt(Value)}
    3. End Operator
    Offensichtlich muss bei mehr als einem Operator jeder verwendete Typ seinen eigenen Operator bekommen.
    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!
    @ErfinderDesRades
    Schmuddelcode finde ich das absolut nicht. Der Compiler prüft ja vorher, ob der Wert tatsächlich im kleineren Datentyp Platz hat.
    Es gibt kein Suffix für Byte. Wenn die implizite Konvertierung nicht erlaubt wäre, müsste man z.B. Dim A As Byte = CByte(123) schreiben, weil Dim A As Byte = 123 nicht erlaubt wäre. In C# geht das übrigens auch: byte a = 123; kompiliert problemlos. In C# gibt's übrigens auch kein Suffix für Short.

    @RodFromGermany
    Aber es liegt nicht strikt an der Anzahl:

    VB.NET-Quellcode

    1. Public Shared Sub Main(Args As String())
    2. Dim I As Foo
    3. I = 123
    4. I = 123S
    5. I = 123L
    6. I = DateTime.Now
    7. End Sub
    8. Public Structure Foo
    9. Public Value As Integer
    10. Public Shared Widening Operator CType(Value As DateTime) As Foo
    11. Return New Foo With {.Value = Value.Day}
    12. End Operator
    13. Public Shared Widening Operator CType(Value As Integer) As Foo
    14. Return New Foo With {.Value = Value}
    15. End Operator
    16. End Structure

    Hier funktioniert alles problemlos. Man kann auch noch einen Operator CType von Byte zu Foo hinzufügen. Klappt immer noch. Erst wenn der Operator CType von String zu Foo hinzugefügt wird, funktioniert die implizite Konvertierung von Long zu Integer nicht mehr.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Und es wird noch viel lustiger: Füge neben String und Integer noch einen Widening Operator CType(Single) hinzu und rufe ihn mit 123L auf. Das lässt sich bei mir kompilieren. Interessant ist daran, dass der Compiler heimlich 123L in 123F ändert (gesehen in ILSpy)... :/

    Vielleicht kann man aus dem Quellcode des Compilers (github.com/dotnet/roslyn) ersehen, woran sich der Compiler im Fall "String + Integer" stört.
    Gruß
    hal2000