Lustiges in .Net

Dim converter As TypeConverter = TypeDescriptor.GetConverter(GetType(Long))

If Not converter.CanConvertFrom(GetType(Long)) then
    Return False
Else
    Return True
End If

Dieser Schnipsel zeigt eine lustige Eigenheit in .Net. Wenn man sich über den TypeDescriptor einen Converter für beispielsweise Long holt und diesen fragt, ob er in den selben Typ (Long) umwandeln kann, dann gibt die Funktion CanConvertFrom False zurück. Etwas seltsam, denn Konvertieren kann man das ja, bzw. einfach zuweisen ohne zu konvertieren. Ich finde diesen Rückgabewert etwas verwunderlich!

Warum man AndAlso/OrElse in VB.Net verwenden sollte…

VB.Net hat neben den Operatoren And und Or auch die Operatoren AndAlso und OrElse. Diese werden auch als Kurzschluss-Operatoren bezeichnet. Der Unterschied zwischen den Operatoren ist, dass AndAlso und OrElse die Überprüfung abbrechen, sobald das Ergebnis klar ist.

Ich wusste lange nicht, dass es für diesen Zweck extra Operatoren gibt und dachte, dass dieses Verhalten der Standard ist. Und meiner Meinung nach, sollte es der Standard sein. Denn wenn der erste Teil einer And-Bedingung False ist, dann ist das Ergebnis des zweiten Teils irrelevant für das Gesamtergebnis der Bedingung. Warum sollte man also besser immer AndAlso und OrElse verwenden?

Punkt 1: Performance.
Da bereits nach der ersten Bedingung einer If-Bedingung das Ergebnis bekannt sein kann, müssen die anderen Prüfungen nicht mehr durchgeführt werden. Gerade wenn in der Überprüfung beispielsweise Datenbankzugriffe ausgeführt werden, kann viel Performance gespart werden. Aber auch bei kleinen Prüfungen kann Performance gespart werden. Nämlich dann, wenn viele kleinen Überprüfungen nicht mehr ausgeführt werden.

Punkt 2: Subtile Bugs (Unoffensichtliches Verhalten)
Das der zweite Teil eine And-Bedingung auch ausgeführt wird, wenn der erste Teil False war, ist nicht offensichtlich und verleitet zu Fehlern. Deshalb sollte immer AndAlso und OrElse verwendet werden. Ein Beispiel:

Private Function Test(obj As Object) As Boolean
    If obj Is Nothing Then
        Throw New NullReferenceException("Objekt nicht gesetzt.")
    End If
    
    Return True
End Function

Public Sub Main()
    Dim obj As Object = Nothing
    If obj IsNot Nothing And Test(obj) Then
        Console.WriteLine("Okay.")
    End If
End Sub

Die Methode Test wirft eine Exception, wenn die Übergabe Nothing ist. Dies simuliert eine Funktion, welche die Übergabe von Nothing nicht verträgt. Um Fehler direkt abzufangen wird die Variable auf Nothing geprüft und die Methode Test mit der Variablen aufgerufen. Die Ausführung von Main wirft die NullReferenceException, weil der zweite Block ausgeführt wird. Nicht sehr intuitiv. Schreibt man die Main-Methode aber so:

Public Sub Main()
    Dim obj As Object = Nothing
    If obj IsNot Nothing AndAlso Test(obj) Then
        Console.WriteLine("Okay.")
    End If
End Sub

Dann funktioniert es, wie erwartet, indem die Funktion Test nicht ausgeführt wird.

Meine Empfehlung ist also AndAlso und OrElse immer zu verwenden, um solche Bugs zu umgehen und um, wenigstens ein wenig, mehr Performance aus den eigenen Programmen zu holen.

Strings schnell aneinanderhängen

Ich komme aus der Java-Welt und dort hieß es immer, wenn man mehrere Strings aneinander hängen will, muss man StringBuffer/StringBuilder benutzen. Ich habe dies jetzt mal in .Net untersucht und musste feststellen, dass es etwas mehr ist.

In .Net habe ich jetzt einige Optimierungen getestet und mit dem IL-Disassembler angesehen. Und es kam heraus, dass der folgende Ausdruck am schnellsten ist. Außerdem ist er lesbarer als eine vergleichbare Version mit einem StringBuilder.

Dim strHallo as String = "Hallo"
Dim strResult as String

strResult = "<" & strHallo & ">"

Der Compiler optimiert diesen Code, sodass die Funktion String.Concat() aufgerufen wird.  Dabei werden die passenden Überladungen aufgerufen. Bis zu 4 Argumente als Strings. Darüber hinaus wird ein String-Array erstellt, gefüllt und übergeben. Ein weiterer positiver Effekt ist, dass bekannte Strings die verbunden werden sollen, bereits vom Compiler zusammengefügt werden.

Der folgende Code wird allerdings nicht optimal ausgeführt, da auch dieser Code auf String.Concat() für 2 Strings zurückgeführt wird. Der Code benötigte bei mir auf meinem Laptop ca. 17 Sekunden.

Dim strResult As String = ""
For i As Integer = 0 To 100000
    strResult &= "<"
Next i

Der nachfolgende Code mit einem StringBuilder benötigte weniger als 10 Millisekunden und ist auch verständlich.

Dim strResult As String = ""
Dim sb As New StringBuilder()

For i As Integer = 0 To 100000
    sb.Append("<")
Next i

strResult = sb.ToString()

Fassen wir das Ganze zusammen:

  • Soll eine feste Zahl an Strings aneinander gehangen werden, dann kann man den &-Operator benutzen.
  • Konstante Strings werden vom Compiler zusammengefügt.
  • Soll ein String mit einer Schleife zusammengebaut werden, so empfiehlt sich die Benutzung eines StringBuilder!