none
Wertetyp an Lambda-Ausdruck übergeben RRS feed

  • Frage

  • Hallo Community,

    ich würde gerne per Lambda-Ausdruck eine List(Of Point) ändern. Hier ein nicht funktionierdes Beispiel:

    \\\

    Dim pts As New List(Of Point) 
    '...
    pts.ForEach(Sub(pt) 
    pt = New Point(pt.X, -pt.Y) End Sub)

    ///

    Das funktioniert so natürlich nicht, weil es sich bei Point um einen Wertetypen handelt und die Übergabe ByVal geschieht. Nun dachte ich mir, alles kein Problem, ubergibst Du den Parameter einfacher ByRef. Also pts.ForEach(Sub(ByRef pt)... . Das geht aber nicht, da so die Signatur nicht passt.

    Gib es nun eine Möglichkeit trotz dem die Referenz auf den Parameter zu erhalten?

    Ich weiß das man das nomalerweise per Index erledigt, aber obiges Beispiel ist halt nur ein Beispiel.

    Danke für jeden Tipp

    --

    Gruß Scotty

    Samstag, 8. Mai 2010 05:04

Antworten

  • Ok, ich habe eine Lösung gefunden:

    Ich habe ein Klasse RefPoint erstellt. Diese hat nur einem Member Value As Point. Meine Liste ist jetzt eine List(Of RefPoint). Wenn ich jetzt den Lambda-Ausdruck aufrufe erhalte ich eine Referenz auf das Objekt vom Typ RefPoint und kann die Value ändern. Kleiner positiver Nebeneffekt: Wenn ich nun eine Änderung ausführe(so wie in meinem Beispiel), dann ist diese Variante ca. 3mal so schnell als würde man die Werte per Index ändern. Auf die Idee bin ich übrigens über den Nullable Value Typ System.Nullable(Of Point) oder kurz Point? gekommen .Nullable ist aber selber auch eine Structure deswegen funktionierte das nicht direkt.

    --

    Gruß Scotty

    Samstag, 8. Mai 2010 06:09

Alle Antworten

  • Ok, ich habe eine Lösung gefunden:

    Ich habe ein Klasse RefPoint erstellt. Diese hat nur einem Member Value As Point. Meine Liste ist jetzt eine List(Of RefPoint). Wenn ich jetzt den Lambda-Ausdruck aufrufe erhalte ich eine Referenz auf das Objekt vom Typ RefPoint und kann die Value ändern. Kleiner positiver Nebeneffekt: Wenn ich nun eine Änderung ausführe(so wie in meinem Beispiel), dann ist diese Variante ca. 3mal so schnell als würde man die Werte per Index ändern. Auf die Idee bin ich übrigens über den Nullable Value Typ System.Nullable(Of Point) oder kurz Point? gekommen .Nullable ist aber selber auch eine Structure deswegen funktionierte das nicht direkt.

    --

    Gruß Scotty

    Samstag, 8. Mai 2010 06:09
  • Nachtrag:

    Habe meine Klasse eben mal zu einem kleinen fixen universell einsetzbaren Helfer um gestaltet.

    Public Class RefType(Of T)

        Public Value As T

        Public Sub New(ByVal value As T)

            Me.Value = value

        End Sub

    End Class

    Wer es mal austesten will, hier meine Test Routine:

            Dim pts1 As New List(Of RefType(Of Point))

            For n = 0 To 4999999

                pts1.Add(New RefType(Of Point)(New Point(n, n)))

            Next

            Dim stpw As New System.Diagnostics.Stopwatch

            stpw.Start()

            pts1.ForEach(Sub(pt)

                             pt.Value.Y = -pt.Value.Y

                         End Sub)

            stpw.Stop()

            Console.WriteLine("5.000.000 elements via RefType : {0}ms", stpw.ElapsedMilliseconds)

            Dim pts2 As New List(Of Point)

            For n = 0 To 4999999

                pts2.Add(New Point(n, n))

            Next

            stpw.Reset()

            stpw.Start()

            For i = 0 To pts2.Count - 1

                pts2(i) = New Point(pts2(i).X, -pts2(i).Y)

            Next

            stpw.Stop()

            Console.WriteLine("5.000.000 elements via Index   : {0}ms", stpw.ElapsedMilliseconds)

    Die Zeiten bei mir:

    5.000.000 elements via RefType : 76ms

    5.000.000 elements via Index   : 211ms

    PS: Hoffe diesmal sieht das mit der Formatierung besser aus.

    --

    Gruß Scotty

     

    Samstag, 8. Mai 2010 06:47
  • Hi Karsten,
    du vergleichst hier völlig unterschiedliche Technologien und das Ergebnis ist dementsprechend ohne sinnvolle Aussage.
     
    a)       pts1.ForEach(Sub(pt)

                             pt.Value.Y = -pt.Value.Y

                         End Sub)

     
    In den vorhandenen Objekten werden Werte geändert.
     
    b)      For i = 0 To pts2.Count - 1

                pts2(i) = New Point(pts2(i).X, -pts2(i).Y)

            Next

     
    Es werden neue Objekte instanziiert. Die Speicherplatzzuweisung dauert natürlich länger als eine einfache Änderungen eines Wertes auf einem bereits zugewiesenen Speicherplatz.
     

    --
    Viele Gruesse
     
    Peter
    Dienstag, 18. Mai 2010 05:06
  • Hallo Peter,

    anscheinend hast Du nicht verstanden, was ich aufzeigen wollte. Weder in einer ForEach noch Index-Schleife kann man die Werte X und Y Werte nicht direkt zu weisen. Vielleicht hast Du nicht gesehen, dass pts2 eine List(Of Point) ist. So etwas funktioniert also nicht:

    b)      For i = 0 To pts2.Count - 1

                pts2(i).Y = -pts2(i).Y

            Next

     

    Fehler 1 Der Ausdruck ist ein Wert und kann nicht als Ziel einer Zuweisung verwendet werden.

    Die einzige mögliche Zuweisung kann nur wie in meinem Beispiel geschen. Und in einer ForEach-Schleife geht es gar nicht, da die Schleifenvariable ByVal übergeben wird.

    Du hast in sofern Recht dass es 2 verschiedene Techniken sind, aber genau hier entsteht der Vergleich. Die RefType-Variante ist schneller als eine normale List(of Point) zu benutzen und dort die Werte via Index zu ändern. Selbst wenn ich anstelle einer List(Of Point) eine List(Of RefType(Of Point)) benutze hätte,ist der Weg über den Index langsamer. Bei mir knapp um den Faktor 2. Ich komme bei ForEach auf ca. 75ms, mit Index auf ca. 140ms. Warum das so ist kann ich nicht sagen.

    Was den Test mit den 5Mio. Elementen angeht, so sind dass für mich noch relativ kleine Listen. Fakt ist nur das diese Listen mehrfach manipuliert werden und das muss so schnell gehen wie nur möglich. Deswegen findet ein Vergleichstest in Bezug auf die Geschwindigkeit statt. Dazu kann ich eine "herkömmliche" Variante durchaus mit einer "spezialisierten" Variante vergleichen.

    --

    Gruß Scotty

    Dienstag, 18. Mai 2010 07:04
  • Hi Karsten,
    ich bekomme keine Probleme bzw. habe das Problem nicht verstanden. Hier mal meine Demo:
     

    Module Module1<o:p></o:p>

    <o:p> </o:p>

      Sub Main()<o:p></o:p>

        '<o:p></o:p>

        Dim pts1 As New List(Of RefType(Of Point))<o:p></o:p>

        For n = 0 To 4999999<o:p></o:p>

          pts1.Add(New RefType(Of Point)(New Point(n, n)))<o:p></o:p>

        Next<o:p></o:p>

        Dim stpw As New System.Diagnostics.Stopwatch<o:p></o:p>

        stpw.Start()<o:p></o:p>

        pts1.ForEach(Sub(pt)<o:p></o:p>

                       pt.Value.Y = -pt.Value.Y<o:p></o:p>

                     End Sub)<o:p></o:p>

        stpw.Stop()<o:p></o:p>

        Console.WriteLine("5.000.000 elements via RefType : {0}ms", stpw.ElapsedMilliseconds)<o:p></o:p>

        '<o:p></o:p>

        Dim pts2 As New List(Of Point)<o:p></o:p>

        For n = 0 To 4999999<o:p></o:p>

          pts2.Add(New Point(n, n))<o:p></o:p>

        Next<o:p></o:p>

        '<o:p></o:p>

        stpw.Reset()<o:p></o:p>

        stpw.Start()<o:p></o:p>

        For i = 0 To pts2.Count - 1<o:p></o:p>

          pts2(i) = New Point(pts2(i).X, -pts2(i).Y)<o:p></o:p>

        Next<o:p></o:p>

        stpw.Stop()<o:p></o:p>

        Console.WriteLine("5.000.000 elements via Index   : {0}ms", stpw.ElapsedMilliseconds)<o:p></o:p>

        '<o:p></o:p>

        Dim pts3 As New List(Of Point)<o:p></o:p>

        For n = 0 To 4999999<o:p></o:p>

          pts3.Add(New Point(n, n))<o:p></o:p>

        Next<o:p></o:p>

        '<o:p></o:p>

        stpw.Reset()<o:p></o:p>

        stpw.Start()<o:p></o:p>

        For i = 0 To pts2.Count - 1<o:p></o:p>

          pts2(i).Y = -pts2(i).Y<o:p></o:p>

        Next<o:p></o:p>

        stpw.Stop()<o:p></o:p>

        Console.WriteLine("5.000.000 elements direct   : {0}ms", stpw.ElapsedMilliseconds)<o:p></o:p>

    <o:p> </o:p>

        ' Anzeige anhelten<o:p></o:p>

        Console.ReadKey()<o:p></o:p>

      End Sub<o:p></o:p>

    <o:p> </o:p>

    End Module<o:p></o:p>

    <o:p> </o:p>

    Public Class RefType(Of T)<o:p></o:p>

      Public Value As T<o:p></o:p>

      Public Sub New(ByVal value As T)<o:p></o:p>

        Me.Value = value<o:p></o:p>

      End Sub<o:p></o:p>

    End Class<o:p></o:p>

    <o:p> </o:p>

    Public Class Point<o:p></o:p>

      Public Sub New(ByVal x As Double, ByVal y As Double)<o:p></o:p>

        Me.X = x<o:p></o:p>

        Me.Y = y<o:p></o:p>

      End Sub<o:p></o:p>

      Public Property X As Double<o:p></o:p>

      Public Property Y As Double<o:p></o:p>

    End Class

     


    --
    Viele Gruesse

    Peter<o:p></o:p>

    Dienstag, 18. Mai 2010 17:41
  •     Dim pts2 As New List(Of Point)

        For i = 0 To pts2.Count - 1

          pts2(i).Y = -pts2(i).Y

        Next

     

    Public Class Point

      Public Sub New(ByVal x As Double, ByVal y As Double)

        Me.X = x

        Me.Y = y

      End Sub

      Public Property X As Double

      Public Property Y As Double

    End Class

     

    Hallo Peter,

    ja so schon, Du hast ja auch eine neue Klasse erstellt. Ich habe aber die System.Drawing.Point-Structure benutzt und damit ist die Zuweisung nicht möglich. Mit einer solchen speziellen Klasse kann man den Wert auch via ForEach ändern, da es sich um einen Referenztyp handelt. Mit der Struktur ist das nicht möglich,da es sich um einen Wertetyp handelt und beim Durchlaufen der ForEach-Schleife nur Kopien geliefert werden. Bei einem Referenztyp wird zwar auch nur eine Kopie geliefert, jedoch ist dies eine Kopie der Referenz und somit kann man das Objekt auch beeinflussen. Aber das ist Dir sicherlich bekannt.

    --

    Gruß Scotty

    Mittwoch, 19. Mai 2010 07:08
  •     Ich habe aber die System.Drawing.Point-Structure benutzt
    Hi Karsten,
    daran hatte ich nicht gedacht, da du es nicht geschrieben hast. Ich bin wegen den von dir genutzten Console.WriteLine von einer Konsolen-Anwendung ausgegangen und da fehlt standardmäßig der System.Drawing-Namensraum.
     
    Und warum kannst du nicht die Strukturen nutzen? Hier mal eine Demo, wie das noch viel schneller geht, wenn man direkt in die Felder der Struktur schreibt:
     
    Imports System.drawing
     
    Module Module1
     
      Sub Main()
        '
        Dim pts1 As New List(Of RefType(Of Point))
        For n = 0 To 4999999
          pts1.Add(New RefType(Of Point)(New Point(n, n)))
        Next
        Dim stpw As New System.Diagnostics.Stopwatch
        stpw.Start()
        pts1.ForEach(Sub(pt)
                       pt.Value.Y = -pt.Value.Y
                     End Sub)
        stpw.Stop()
        Console.WriteLine("5.000.000 elements via RefType : {0}ms", stpw.ElapsedMilliseconds)
        '
        Dim pts2 As New List(Of Point)
        For n = 0 To 4999999
          pts2.Add(New Point(n, n))
        Next
        '
        stpw.Reset()
        stpw.Start()
        For i = 0 To pts2.Count - 1
          pts2(i) = New Point(pts2(i).X, -pts2(i).Y)
        Next
        stpw.Stop()
        Console.WriteLine("5.000.000 elements via Index   : {0}ms", stpw.ElapsedMilliseconds)
        '
        Dim pts3 As New List(Of Point)
        For n = 0 To 4999999
          pts3.Add(New Point(n, n))
        Next
        '
        stpw.Reset()
        stpw.Start()
        For i = 0 To pts2.Count - 1
          Dim p = pts3(i)
          p.Y = -p.Y
        Next
        stpw.Stop()
        Console.WriteLine("5.000.000 elements direct   : {0}ms", stpw.ElapsedMilliseconds)
     
        ' Anzeige anhelten
        Console.ReadKey()
      End Sub
     
    End Module
     
    Public Class RefType(Of T)
      Public Value As T
      Public Sub New(ByVal value As T)
        Me.Value = value
      End Sub
    End Class
     

    --
    Viele Gruesse
     
    Peter
    Mittwoch, 19. Mai 2010 20:06
  • Und warum kannst du nicht die Strukturen nutzen? Hier mal eine Demo, wie das noch viel schneller geht, wenn man direkt in die Felder der Struktur schreibt:

    Hallo Peter weils das nicht geht. Schau hier:

    Dim pts As New List(Of Point)

    pts.Add(New Point(0, 0))

    Dim p = pts(0)

    p.X = 10

     

    Console.WriteLine(p.X)

    Console.WriteLine(pts(0).X)

    Ausgabe:

    10

    0

    Eine Struktur ist ein Wertetyp. Mit der Zuweisung p = pts(0) erhält man nur ein Kopie. Wenn das bei Dir funktioniert, befürchte ich Du hast Deine Point-Klasse noch im Projekt. Der reine Import des System.Drawing-Namespace reicht nicht aus. Entferne mal die Klasse oder deklariere Deine List so:

    Dim pts As New List(Of System.Drawing.Point)

    Dann wirst Du sehen dass das nicht geht.

    P.S.: Was das Benutzen der Console-Klasse angeht, so ist das Macht der Gewohnheit.

    --

    Gruß Scotty

    Donnerstag, 20. Mai 2010 08:38
  • Hi Karsten,
    wenn die Daten wieder in der Liste gebraucht werden, müssen sie zurückgeschrieben werden, was immer noch schneller geht als eine Neuzuweisung von Speicherplatz:
     

    Dim pts As New List(Of Point)

    pts.Add(New Point(0, 0))

    Dim p0 = pts(0)

    p0.X = 10

    pts(0) = p0

    Console.WriteLine(p0.X)

    Console.WriteLine(pts(0).X)

    "Karsten Sosna" schrieb im Newsbeitrag news:c7abded8-e6c7-411c-9950-d407521c1e72...
    Und warum kannst du nicht die Strukturen nutzen? Hier mal eine Demo, wie das noch viel schneller geht, wenn man direkt in die Felder der Struktur schreibt:

    Hallo Peter weils das nicht geht. Schau hier:

    Dim pts As New List(Of Point)

    pts.Add(New Point(0, 0))

    Dim p = pts(0)

    p.X = 10

     

    Console.WriteLine(p.X)

    Console.WriteLine(pts(0).X)

    Ausgabe:

    10

    0

    Eine Struktur ist ein Wertetyp. Mit der Zuweisung p = pts(0) erhält man nur ein Kopie. Wenn das bei Dir funktioniert, befürchte ich Du hast Deine Point-Klasse noch im Projekt. Der reine Import des System.Drawing-Namespace reicht nicht aus. Entferne mal die Klasse oder deklariere Deine List so:

    Dim pts As New List(Of System.Drawing.Point)

    Dann wirst Du sehen dass das nicht geht.

    P.S.: Was das Benutzen der Console-Klasse angeht, so ist das Macht der Gewohnheit.

    --

    Gruß Scotty

    Donnerstag, 20. Mai 2010 13:21
  • wenn die Daten wieder in der Liste gebraucht werden, müssen sie zurückgeschrieben werden, ...

    Hallo Peter,

    wenn ich das nicht bräuchte, würde ich nicht solche Wege gehen. Schau Dir doch noch mal das Eingangsposting an. Wozu bräuchte ich sonst einen Verweis? Ich hatte auch etwas später geschrieben: "Fakt ist nur das diese Listen mehrfach manipuliert werden ...".

    Das ist leider zu weit ausgeartet und dabei ist meine eigentliche Anfrage untergegangen. Es ging um den Parameter eines Lambda-Ausdrucks und den kann man nicht manipulieren, wenn es sich um eine Wertetyp handelt.

    Da ich glaube dass es an dieser Stelle keinen Sinn mehr hat hier gegenseitig Beispielcode auszutauschen und ich ja meine Lösung(Lösungsansatz) schon gefunden habe, können wir es an dieser Stelle gerne beenden.

    --

    Gruß Scotty

    Donnerstag, 20. Mai 2010 13:46
  • Hi Karsten,
    die NNTP-Bridge hatte mehrmals mein Windows abgeschossen (100 % CPU). Letztes Mal war auch noch der Finger abgerutscht und das Posting zu früh losgeschickt

    wenn die Daten wieder in der Liste gebraucht werden, müssen sie zurückgeschrieben werden, was immer noch schneller geht als eine Neuzuweisung von Speicherplatz:
     
    Dim pts As New List(Of Point)
    pts.Add(
    New Point(0, 0))
    Dim p0 = pts(0)
    p0.X = 10
    pts(0) = p0

     

     

     

    Console.WriteLine(p0.X)
    Console.WriteLine(pts(0).X)

    Übrigens hat dein Programmschnipsel das gleiche Problem. Schau dir mal an:

    Dim pts1a As New List(Of RefType(Of Point))
    pts1a.Add(
    New RefType(Of Point)(New Point(20, 20)))
    Console.WriteLine(pts1a(0).Value.X)
    pts1a(0).Value.Y = -pts1a(0).Value.Y
    Console.WriteLine(pts1a(0).Value.X)

    Damit bringt dein geposteter Meßaufbau kein bracuhbaren Ergebnisse.

    --
    Viele Gruesse

    Peter

     

     

    Donnerstag, 20. Mai 2010 16:28
  • Übrigens hat dein Programmschnipsel das gleiche Problem. Schau dir mal an:

    Dim pts1a As New List(Of RefType(Of Point))
    pts1a.Add(
    New RefType(Of Point)(New Point(20, 20)))
    Console.WriteLine(pts1a(0).Value.X)
    pts1a(0).Value.Y = -pts1a(0).Value.Y
    Console.WriteLine(pts1a(0).Value.X)

    Damit bringt dein geposteter Meßaufbau kein bracuhbaren Ergebnisse.

    Hallo Peter,

    mein Codebeispiel mach genau das was es soll. Das macht Deins übrigens auch. Jedoch wenn Du Y änderst dann solltest Du Dir auch Y anschauen und X. ;=)

    Damit sind wohl Deine beiden Aussagen hinfällig.

    --

    Gruß Scotty

    Freitag, 21. Mai 2010 07:06
  • Hi Karsten,
    ist das peinlich. Du hast natürlich Recht und ich muss mir das nochmals alles in Ruhe zu Gemüte ziehen.

    --
    Viele Gruesse
     
    Peter
    Freitag, 21. Mai 2010 09:42