Mehrzeilige Lambdas in VB2010 - umdenken beim Coden!

by Klaus Löffelmann 29. Dezember 2009 09:02

Visual Basic 2008 führte Lambda-Funktionen ein - auch unter dem Namen anonyme Methoden bekannt - und mit ihnen eine Erleichterung im Umgang mit Delegaten: Lambdas sind Methoden, die keinen Methodennamen besitzen; stattdessen können lediglich ihre Programmadressen in Delegat-Variablen gespeichert oder diese direkt an Methoden übergeben werden, die Delegaten einer bestimmten Signatur als Parameter erwarten. Bis Visual Basic 2008 war der Umgang mit Lambdas eingeschränkt, da diese einerseits nur einzeilig sein durften und andererseits grundsätzlich Funktionsergebnisse zurückliefern mussten.

Die ForEach-Methode der generischen Liste zeigt einfach und plausibel den Umgang mit Lambdas: Geht es darum, möglichst schnell die Elemente einer generischen Liste zu verarbeiten, eignet sich die ForEach-Methode im Zusammenhang mit einer Lambda-Funktion besonders, da sie - im Gegensatz zum herkömmlichen For Each-Konstrukt - intern nicht den Umweg über GetEnumerator zum Aufzählen aller Elemente der Liste nehmen muss, sondern direkt auf die Listenelemente zugreifen kann.

Um alle Elemente einer Liste beispielsweise im Debug-Fenster mithilfe einer Lambda-Funktion auszugeben, kann man sich folgendem Code bedienen:

    Sub LambdaDemo()
        Dim namenListe As New List(Of String) From {"Peter", "Klaus", "Tanja", "Sonja"}
        'Anstelle eines Delegaten:
        Dim writeToDebugDelegate As New Action(Of String)(AddressOf WriteToDebug)
        namenListe.ForEach(writeToDebugDelegate)

        'Schreiben wir das hier direkt:
        namenListe.ForEach(Function(item) WriteToDebug(item))
    End Sub

    Shared Function WriteToDebug(ByVal text As String) As Boolean
        Debug.Print(text)
        Return True
    End Function

Action(of String) ist in diesem Fall der Delegattyp, den die ForEach-Methode als Delegatvariable erwartet. Und hier sind Lambdas eine Erleichterung, weil eine Lambda-Funktion der ForEach-Methode auch direkt als Parameter übergeben werden kann.

Bis Visual Basic 2008 war man allerdings, wie schon gesagt, bei Lambdas auf Methoden mit Rückgabeparameter beschränkt. Aus diesem Grund gab es bis zu dieser VB-Version auch nicht die Möglichkeit, zum Beispiel ein Konstrukt wie Debug.Print direkt in der Lambda-Funktion anzugeben, da Debug.Print eben eine Sub, also eine Methode ohne Rückgabewert darstellt.

Ab Visual Basic 2010 ist das anders. Hier ist dieses Konstrukt

namenListe.ForEach(Sub(item) Debug.Print(item))

ebenso erlaubt, wie mehrzeilige Lambdas:

        namenListe.ForEach(Sub(item)
                               'Hier können jetzt mehrere Codezeilen platziert werden
                               Debug.Print(item)
                           End Sub)

Der Einsatz von Lambdas wird Visual Basic-Programme in Zukunft deutlich anders ausschauen lassen. Lambdas haben hier das Potential, einerseits dem Entwickler Arbeit abzunehmen, weil er für viele Zwecke nicht mehr den Umweg über Delegaten nehmen muss, und das spart Codezeilen und Coding-Zeit. Andererseits wirkt der Code auch aufgeräumter, da Code an der Stelle steht, wo er passiert.

Das hat ab dem .NET Framework 4.0 umsomehr Bedeutung, da Delegaten hier verstärkt zum Einsatz kommen, wenn die Codeausführung parallelisiert, also automatisch auf mehrere Prozessoren oder Prozessor-Cores verteilt werden muss, wie das folgende Beispiel zeigt:

        Parallel.ForEach(namenListe, Sub(item)
                                         'Elemente parallel verarbeiten.
                                         'ACHTUNG: Das passiert durch die Gleichzeitigkeit
                                         'nicht mehr sequentiell sondern durcheinander!
                                         Debug.Print(item)
                                     End Sub)

In diesem Beispiel muss übrigens der Namespace System.Threading.Tasks importiert werden.

In der Praxis kann der Umgang mit Lambdas dann folgender Maßen ausschauen. In dem folgenden Beispiel geht es um die Validierung einer Eingabe in einem Steuerelemente, die, wenn sie fehlschlägt, eine Exception zurückliefert aufgrund derer dann ein Tooltip mit dem entsprechenden Fehlermeldungstext direkt neben dem Steuerelement ausgegeben werden soll, das die Fehlermeldung erzeugt hat:

    Protected Overrides Sub OnValidating(ByVal e As System.ComponentModel.CancelEventArgs)

        MyBase.OnValidating(e)
        If e.Cancel Then
            Return
        End If

        'ValidateInput überprüft, ob die Eingabe i.O. geht, und liefert
        'bei einem Fehler eine Exception zurück, die letzten Endes
        'auch den Fehlertext ergibt.
        Dim valRes = ValidateInput(myValueControl.Value.ToString)
        If valRes IsNot Nothing Then
            e.Cancel = True

            'Bei Bedarf, kann hier ein Warnton ausgegeben werden.
            If myBeepOnFailedValidation Then
                Beep()
            End If

            'Die Fehlermeldung soll in einem Tooltip neben
            'dem Steuerelement für eine gewisse Zeit zu sehen sein.
            Dim tt As New ToolTip()

            'Steuerelement malen wir selbst.
            tt.OwnerDraw = True

            'Soll sich Ausblenden.
            tt.UseFading = True
            tt.UseAnimation = True

            'Dank Mehrzeiligen Lambdas, können wir den Drawing-Handler Code
            'direkt dem Draw-Ereignis übergeben.
            AddHandler tt.Draw, Sub(sender As Object, de As DrawToolTipEventArgs)
                                    Dim pen As New Pen(Brushes.Black, 1)
                                    'Point-Array clonen und Koordinaten anpassen

                                    Dim path As New GraphicsPath()
                                    Dim p1, p2 As PointF
                                    p1 = PointF.Empty

                                    'Die Koordinaten des Rahmen des
                                    'Tooltipps stehen in einer List(Of PointF),
                                    'die wir mit der ForEach-Methode, und wieder
                                    'mit einem mehrzeiligen Lambda durchiterieren.
                                    myToolTipShapeCoordinates.ForEach(
                                        Sub(item)

                                            'Koordinaten des Tooltips an
                                            'die des Steuerelements anpassen:
                                            Dim x = item.X
                                            Dim y = item.Y
                                            If x = -1 Then
                                                x = de.Bounds.X + de.Bounds.Width - 1
                                            Else
                                                x += de.Bounds.X
                                            End If

                                            If y = -1 Then
                                                y = de.Bounds.Y + de.Bounds.Height - 1
                                            Else
                                                y += de.Bounds.Y
                                            End If
                                            p1 = p2
                                            p2 = New PointF(x, y)
                                            If p1 <> Point.Empty Then
                                                path.AddLine(p1, p2)
                                            End If
                                        End Sub)

                                    'Zum Malen auf AntiAlias stellen: sieht "weicher" aus.
                                    de.Graphics.SmoothingMode = SmoothingMode.AntiAlias
                                    path.CloseFigure()

                                    'Hintergrund, Umrandung und Text des Tooltips malen:
                                    de.Graphics.FillPath(New SolidBrush(Color.AliceBlue), path)
                                    de.Graphics.DrawPath(Pens.Black, path)
                                    de.Graphics.DrawString(de.ToolTipText, New Font(FontFamily.GenericSansSerif,
                                                                                   8, FontStyle.Regular),
                                                                               Brushes.Black,
                                                            New RectangleF(de.Bounds.X + 15, de.Bounds.Y + 5,
                                                                           de.Bounds.Width - 15, de.Bounds.Height - 5))
                                End Sub

            'Tooltip ausgeben. Das erst triggert den obenstehenden Code,
            'da jetzt erst das Draw-Ereignis des Tooltips ausgelöst wird,
            'dem dieser Code mit AddHandler zugeordnet wurde.
            tt.Show("Die Eingabe hatte einen Fehler:" & vbNewLine & vbNewLine &
                    valRes.Message, Me, Me.Width - 10, Me.Height \ 2, Me.ExceptionBalloonDuration)
        End If
    End Sub

 

Tags: , , , ,

Kommentare

08.05.2010 14:20:39 #

Mariana

I have a good fresh joke for you! How do you get holy water? Boil the hell out of it.

Mariana Vereinigte Staaten von Amerika

20.05.2010 00:05:25 #

Wahdo

If you have to do it, you might as well do it right.

Wahdo Vereinigte Staaten von Amerika

27.05.2010 16:36:27 #

La Martina

große Artikel, ich denke, jetzt werde ich in VS2010 zu schauen, um selbst zu sehen.

La Martina Vereinigte Staaten von Amerika

21.06.2010 13:50:32 #

Latest Articles

Thanks for posting about this, I would love to read more about this topic.

Latest Articles Vereinigte Staaten von Amerika

25.06.2010 11:37:12 #

vaniqa

dankkkk

vaniqa Deutschland

09.07.2010 10:08:33 #

chanel

Ihr Beitrag hat mich zu stoppen und schauen auf Ihrem Blog. Du bist ein großer Blogger und das ist ein wunderbares Blog.

chanel Norwegen

Kommentar schreiben




  Country flag

biuquote
  • Kommentar
  • Live Vorschau
Loading



Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen