List(Of T).ForEach(Action(Of T)) versus herkömmliche ForEach-Schleife

  • VB.NET

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von Arby.

    List(Of T).ForEach(Action(Of T)) versus herkömmliche ForEach-Schleife

    Bezug: [VB 2010] Event aus einer Klasse, deren Objekte sich in einer List befinden

    aus Bezug schrieb:

    Ich habe das mal in etwa so gelöst:

    VB.NET-Quellcode

    1. ListOfObject.ForEach(Sub(x) AddHandler x.MyEvent, AddressOf MyEventHandler)


    @Higlav: Es ist ein bisschen komisch/ungewöhnlich, eine simple ForEach-Schleife als Lambda zu formulieren.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

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

    Niko Ortner schrieb:

    @Higlav Es ist ein bisschen komisch/ungewöhnlich, eine simple ForEach-Schleife als Lambda zu formulieren.
    Ich find das ganz normal.
    ich finds eher umständlich, was man als Einzeiler formulieren kann, über 3 Zeilen zu verstreuen.

    Aber kompakter Code gehört auch zu meine Programming-Guidelines: Eine Methode sollte auf einen Bildschirm passen - besser noch die ganze Klasse.

    VB.NET-Quellcode

    1. Sub Foo()
    2. For Each i In Liste
    3. AddHandler i.Event, AddressOf Handler
    4. Next
    5. End Sub
    6. Sub Bar()
    7. Liste.ForEach(Sub(i) AddHandler i.Event, AddressOf Handler)
    8. End Sub

    Also persönlich finde ich, dass ersteres besser lesbar ist, weil man die Schleife auf den ersten Blick sieht. Mal ganz davon abgesehen, dass bei letzterem eine Lambda-Methode generiert wird, die bei jedem Aufruf ausgeführt wird.

    Das kann man jetzt noch ausführen:
    Bei letzterem müsste man zweimal iterieren:

    VB.NET-Quellcode

    1. Sub Bar()
    2. Dim Liste As New List(Of T)
    3. Enumerable.Range(0, ...).ToList.ForEach(Sub(i) Liste.Add(New T))
    4. Liste.ForEach(Sub(i) AddHandler i.Event, AddressOf Handler)
    5. End Sub
    Das hat jetzt noch den unschönen Umweg über ToList, weil IEnumerable(Of T) keine ForEach-Erweiterung hat.

    Oder man verwendet eine mehrzeilige Lambda-Methode:

    VB.NET-Quellcode

    1. Sub Bar()
    2. Dim Liste As New List(Of T)
    3. Enumerable.Range(0, ...).ToList.ForEach(Sub(i)
    4. Dim Temp As New T
    5. AddHandler Temp.Event, AddressOf Handler
    6. Liste.Add(Temp)
    7. End Sub)
    8. End Sub
    Dadurch gewinnt man, dass nur eine Schleife läuft, aber dann sind's mehr Zeilen.

    Wobei man dann auch gleich so schreiben kann, wenn weniger Zeilen besser sind:

    VB.NET-Quellcode

    1. Dim Temp As New T : AddHAndler Temp.Event, AddressOf Handler : Liste.Add(Temp)
    Da kommt man dann halt schnell ins horizontale Scrollen (und das ist noch schlimmer).

    PS: Wenn das für diesen Thread zu sehr OffTopic ist, wäre es super, einen separaten Thread aufzumachen. Ich würde das nämlich gerne ein bisschen diskutieren.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    naja - deine letzten Samples driften bischen ins Absurde, da geb ich dir recht ;)

    Also mit Augenmaß, und nicht mit Gewalt jetzt olle ForEach überall abzuschaffen versuchen.

    Und mit Gewalt - (und ':') alles in eine Zeile quetschen wird auch recht bald grotesk.
    Tatsächlich verwende ich ':' aber durchaus gelegentlich, um Dinge zu "Einzeilisieren", etwa

    VB.NET-Quellcode

    1. Public Class MainWindowViewModel : Inherits NotifyPropertyChanged
    aber das ist sone Mikro-Optimierung, und betrifft nur Code-Optik - da breche ich bestimmt kein Streit vom Zaun für.
    Oder sowas:

    VB.NET-Quellcode

    1. Private Sub _ItemsControl_PreviewKeyDown(sender As Object, e As KeyEventArgs) Handles _ItemsControl.PreviewKeyDown
    2. Dim focused = _Moveables.Last
    3. Select Case e.Key
    4. Case Key.Up : focused.Y -= StepSize
    5. Case Key.Right : focused.X += StepSize
    6. Case Key.Down : focused.Y += StepSize
    7. Case Key.Left : focused.X -= StepSize
    8. Case Else : Return
    9. End Select
    10. e.Handled = True
    11. End Sub
    ich finds besser, aber wer so schreiben will
    expander

    VB.NET-Quellcode

    1. Private Sub _ItemsControl_PreviewKeyDown(sender As Object, e As KeyEventArgs) Handles _ItemsControl.PreviewKeyDown
    2. Dim focused = _Moveables.Last
    3. Select Case e.Key
    4. Case Key.Up
    5. focused.Y -= StepSize
    6. Case Key.Right
    7. focused.X += StepSize
    8. Case Key.Down
    9. focused.Y += StepSize
    10. Case Key.Left
    11. focused.X -= StepSize
    12. Case Else
    13. Return
    14. End Select
    15. e.Handled = True
    16. End Sub
    ist deshalb auch kein schlechter Mensch ;)

    Ja, nochmal .ForEach(action):
    Stehe ich wie gesagt drauf, und wollte ich nicht nur für List(Of T) haben, sondern für alle IEnumerable(Of T).
    Und mit einer Extension ist das ja auch leicht hinzukriegen:

    VB.NET-Quellcode

    1. ''' <summary>executes the action for each item</summary>
    2. <DebuggerStepThrough(), Extension()> _
    3. Public Sub ForEach(Of T)(ByVal itms As IEnumerable(Of T), ByVal Action As Action(Of T))
    4. For Each itm In itms : Action(itm) : Next
    5. End Sub
    gugge auch [VB 2010] Extensions für Listen - das Form kannste spasseshalber mal auf herkömmliche Weise nachproggen ;)
    Ok, das berühmte Mittelmaß :D
    Beim Inherits und beim Select Case gebe ich Dir recht. Das sieht in der Tat gar nicht schlecht aus, weil nur eine einzige Anweisung da steht.
    Bei Deiner Extension würde ich das schon anders lösen. Zeile 4 würde ich auf 3 Zeilen aufteilen. Es sieht für mich (ist natürlich subjektiv) komsich aus, wenn der For-Block optisch nicht geschlossen wird.
    Ich bin aber nicht vollkommen dagegen. Ich verwende z.B. Einzeiler wie

    VB.NET-Quellcode

    1. EinString.All(AddressOf Char.IsDigit)
    (Nur als Beispiel)
    Also da, wo man sowieso einen Delegaten verwendet hätte oder wo man unschönerweise eine Variable brauchen würde, wenn man eine For(-Each)-Schleife verwenden würde.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    VB.NET-Quellcode

    1. Dim alleSindDigits = EinString.All(AddressOf Char.IsDigit)
    gutes Beispiel, dassichmich in durchaus guter Gesellschaft befinde: Das ganze Linq-Zeugs (hier: die IEnumerable(Of T).All(predicate)-Extension) ist auch drauf ausgerichtet, Vielheiten trotzdem im Einzeiler abzuhandeln.
    Und solange mans nicht übertreibt mit dem Verketten und Verschachteln von Auflistungen, Bedingungen und Selectoren erhöht das die Verständlichkeit des Codes, finde ich.

    ErfinderDesRades schrieb:

    Und solange mans nicht übertreibt mit dem Verketten und Verschachteln von Auflistungen, Bedingungen und Selectoren erhöht das die Verständlichkeit des Codes, finde ich.

    Witzig das von dir zu lesen. Ich hab dein DBExtensions-Projekt gesehen *hust* *zwinker*
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Ich gebe auch mal meine 2 Cents dazu.

    Ich habe mich auch schon mal mit dem ForEach für IEnumerable beschäftigt und mich dabei gefragt, ob es langsam nicht zu viel des guten ist, alles in eine Zeile zu tun.
    Genau zu diesem Thema gab es mal einen MSDN Artikel, wo ein Entwickler rumgeweint hat, dass es ja sooooooooooooooooooooo viel Aufwand sein eine neue Funktion in das Framework zu integrieren.
    1000 verschiedene Qualitätskontrollen und Reviewer blabla...
    Überzeugt hat mich das nicht.
    Den Artikel find ich nicht mehr. Ich glaube er war hier: forums.microsoft.com/MSDN/Show…x?PostID=2386791&SiteID=1. Ist wohl down mittlerweile.

    Was mich eher überzeugt hat ist das:

    Eric Lippert’s Blog: "foreach" vs "ForEach"

    Das Argument mit der gleichen Länge zieht natürlich für VB.Net nicht. Trotzdem finde ich, dass sich die 2 eingesparten Zeilen nicht lohnen. Das normale For Each Konstruct erfasst man halt mit einem Blick.

    Meiner Meinung nach sind die wichtigsten Argumente:
    • unerwünschte Seiteneffekte
    • schwerer zu lesen. Schleife ist nicht mehr als solche erkennbar.


    Nichtsdestoweniger - die beiden vom EDR genannten Fälle (Inherits und Case) sind meiner Meinung nach 2 sehr gute Beispiele, in denen der Code kürzer und lesbarer wird.

    Aber geben wirs doch zu, es ist geil ALLES in eine Zeile zu schreiben, da spart man sich die ganzen temporären Variablen, denen man nen Namen geben müsste ;)
    @Arby:: Watten watten watten? Willst du mal sehen, wie das wirklich geht, mit Linq übertreiben?
    [VB.NET] [Sammelthread] Knobel-Aufgaben, knifflige Algorithmen, elegante Lösungen

    Im Ernst: Ja, in DbGenerator ziehe ich schon arg vom Linq-Leder. Aber Grouping oder Joins sind ohne Linq kaum machbar, und die eine Methode ist 80 Zeilen lang - da habe ich die "Kompromiss-Linie" ziemlich in Richtung Linq verschoben, weil sonst wäre ich wohl locker bei 200 Zeilen gelandet.
    Auch finde ich die Linq-Übungen da nicht soo pervers - wenn man die genau anguckt, versteht man schon, was da gespielt wird.

    Wie gesagt: guck dir mal die verlinkte DotNet-Pearl an ;)

    ErfinderDesRades schrieb:

    Wie gesagt: guck dir mal die verlinkte DotNet-Pearl an

    Ja, ich glaub ich erinner mich. Das müsste so ungefähr der Punkt gewesen sein, an dem ich aufgegeben hatte, dem Thread zu folgen ;)
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.